From a65c0435b71d71ca411efcd901b8e83c19c88be0 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Fri, 29 Nov 2024 10:48:00 +0000 Subject: [PATCH 01/12] Update versions following v2.5.5 release Signed-off-by: Mark S. Lewis --- build.gradle | 2 +- fabric-chaincode-docker/build.gradle | 2 +- .../src/contracts/bare-gradle/build.gradle | 2 +- .../src/contracts/bare-maven/pom.xml | 2 +- .../src/contracts/fabric-ledger-api/build.gradle | 2 +- .../src/contracts/fabric-shim-api/build.gradle | 2 +- .../src/contracts/wrapper-maven/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 654a4d7f5..97b795825 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { id "com.diffplug.spotless" version "6.25.0" } -version = '2.5.5' +version = '2.5.6' // If the nightly property is set, then this is the scheduled main diff --git a/fabric-chaincode-docker/build.gradle b/fabric-chaincode-docker/build.gradle index e5ba5d6c4..324b3a7c4 100644 --- a/fabric-chaincode-docker/build.gradle +++ b/fabric-chaincode-docker/build.gradle @@ -65,5 +65,5 @@ task copyAllDeps(type: Copy) { task buildImage(type: DockerBuildImage) { dependsOn copyAllDeps inputDir = project.file('Dockerfile').parentFile - images = ['hyperledger/fabric-javaenv', 'hyperledger/fabric-javaenv:2.5', 'hyperledger/fabric-javaenv:amd64-2.5.5', 'hyperledger/fabric-javaenv:amd64-latest'] + images = ['hyperledger/fabric-javaenv', 'hyperledger/fabric-javaenv:2.5', 'hyperledger/fabric-javaenv:amd64-2.5.6', 'hyperledger/fabric-javaenv:amd64-latest'] } diff --git a/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle b/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle index b5ea23f8a..5a82082e9 100644 --- a/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle +++ b/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle @@ -24,7 +24,7 @@ repositories { } dependencies { - implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.5' + implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.6' implementation 'org.hyperledger.fabric:fabric-protos:0.3.3' } diff --git a/fabric-chaincode-integration-test/src/contracts/bare-maven/pom.xml b/fabric-chaincode-integration-test/src/contracts/bare-maven/pom.xml index 9740748dd..fccbc9e0c 100644 --- a/fabric-chaincode-integration-test/src/contracts/bare-maven/pom.xml +++ b/fabric-chaincode-integration-test/src/contracts/bare-maven/pom.xml @@ -12,7 +12,7 @@ UTF-8 - 2.5.5 + 2.5.6 diff --git a/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle b/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle index a8d488c30..56c0f2512 100644 --- a/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle +++ b/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle @@ -24,7 +24,7 @@ repositories { } dependencies { - implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.5' + implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.6' implementation 'org.hyperledger.fabric:fabric-protos:0.3.3' } diff --git a/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle b/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle index b128a4c85..06b4af177 100644 --- a/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle +++ b/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle @@ -24,7 +24,7 @@ repositories { } dependencies { - implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.5' + implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.6' implementation 'org.hyperledger.fabric:fabric-protos:0.3.3' implementation 'commons-logging:commons-logging:1.2' implementation 'com.google.code.gson:gson:2.10.1' diff --git a/fabric-chaincode-integration-test/src/contracts/wrapper-maven/pom.xml b/fabric-chaincode-integration-test/src/contracts/wrapper-maven/pom.xml index f9e592382..894443ea7 100644 --- a/fabric-chaincode-integration-test/src/contracts/wrapper-maven/pom.xml +++ b/fabric-chaincode-integration-test/src/contracts/wrapper-maven/pom.xml @@ -12,7 +12,7 @@ UTF-8 - 2.5.5 + 2.5.6 From b640ba38f2e7d571bc6eb96370db266d3e47446b Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Wed, 4 Dec 2024 19:19:04 +0000 Subject: [PATCH 02/12] Push identical image to all Docker registries (#368) The current GitHub Actions workflow builds and pushes separate Docker images for each registry. This results in the images published to each registry having a different hash, despite having the same version tag. This change modifies the Docker publishing job in the release workflow so that a single step builds and publishes the same Docker image to all Docker registries. For reproducability, the timestamp of the image is also set to the last commit timestamp. Signed-off-by: Mark S. Lewis --- .github/workflows/release.yml | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cec6c6661..1a0c86938 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,14 +47,8 @@ jobs: TARGET: ${{ matrix.publish_target }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Publish to docker registries docker.io and ghcr.io + # Publish to Docker registries: docker.io and ghcr.io publishdocker: - strategy: - fail-fast: false - matrix: - DOCKER_REGISTRY: - - 'docker.io' - - 'ghcr.io' runs-on: ubuntu-latest needs: test permissions: @@ -78,24 +72,36 @@ jobs: buildkitd-config-inline: | [worker.oci] max-parallelism = 1 - - name: Login to the ${{ matrix.DOCKER_REGISTRY }} Container Registry + - name: Login to Docker Hub + # If testing on a fork, login error may occur and can be ignored + continue-on-error: true uses: docker/login-action@v3 with: - registry: ${{ matrix.DOCKER_REGISTRY }} - username: ${{ matrix.DOCKER_REGISTRY == 'docker.io' && secrets.DOCKERHUB_USERNAME || github.actor }} - password: ${{ matrix.DOCKER_REGISTRY == 'docker.io' && secrets.DOCKERHUB_TOKEN || secrets.GITHUB_TOKEN }} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Docker meta id: meta uses: docker/metadata-action@v5 with: - images: ${{ matrix.DOCKER_REGISTRY }}/${{ github.repository_owner }}/fabric-javaenv + # If testing on a fork, Docker Hub publish might fail so place it last + images: | + ghcr.io/${{ github.repository_owner }}/fabric-javaenv + docker.io/${{ github.repository_owner }}/fabric-javaenv tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}.{{patch}} + - name: Get Git commit timestamps + run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV - name: Build and push image id: push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: platforms: linux/amd64,linux/arm64 file: fabric-chaincode-docker/Dockerfile @@ -103,3 +109,5 @@ jobs: tags: ${{ steps.meta.outputs.tags }} push: ${{ github.event_name != 'pull_request' }} labels: ${{ steps.meta.outputs.labels }} + env: + SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} From 6db0d3bd4090a580fe4fbfa4525001679c0afd44 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Wed, 11 Dec 2024 17:13:44 +0000 Subject: [PATCH 03/12] Update compatibility information (#369) Also fix some out-of-date links in the contributing documentation. Signed-off-by: Mark S. Lewis --- COMPATIBILITY.md | 74 +++++++++++++++--------------------------------- CONTRIBUTING.md | 16 +++++------ 2 files changed, 31 insertions(+), 59 deletions(-) diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 6c1708b61..57924e508 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -1,68 +1,40 @@ -# Support and Compatibility for fabric-chaincode-java - -Github is used for code base management, issues should reported in the [FABCJ](https://jira.hyperledger.org/projects/FABCJ/issues/) component in JIRA. +# Support and Compatibility +Github is used for code base management and issue tracking. ## Summary of Compatibility -This table shows the summary of the compatibility of the Java libraries, together with the JVM version they require and the Fabric Peer versions they can communicate with. - -| | Fabric Peer v1.4 connectivity | Java 8 VM | Fabric Peer v2.2/2.5 Connectivity | Java 11 VM | -| ----------------------- | ----------------------------- | --------- | ----------------------------- | ---------- | -| Java libraries **v1.4** | Yes | Yes | Yes | Yes | -| Java libraries **v2.1** | Yes | No | Yes | Yes | -| Java libraries **v2.4** | Yes | No | Yes | Yes | -| Java libraries **v2.5** | Yes | No | Yes | Yes | - - -Testing is performed with - - Java v8: OpenJDK - - Java v11: Eclipse Temurin (this has changed from OpenJDK) - - -By default a Fabric Peer v1.4 will create a Java 8 VM, and a Fabric Peer v2.x will create a Java 11 VM. Whilst is the default, the docker image used to host the chaincode and contracts can be altered. Set the environment variable `CORE_CHAINCODE_JAVA_RUNTIME` on the peer to the name of the docker image. For example `CORE_CHAINCODE_JAVA_RUNTIME=example/customJavaRuntime:latest` - -The Java Libraries will connect to the peer whilst running; this is referred to as 'Fabric Peer Connectivity' in the table. For example, whilst the Fabric Peer v1.4 will create a Java 8 environment, if a Java 11 environment was configured, the Java Libraries at v2.5 still function when connecting to the Fabric Peer v1.4. - -## Compatibility - -The key elements are: - -- the version of the Fabric Contract Java libraries used -- the version of the JVM used to run the code -- When starting a chaincode container to run a Smart Contract the version of the runtime that is used is determined by these factors: - -Fabric v1.4.2, and Fabric v2.5.x will, by default, start up docker image to host the chaincode and contracts. The version of the docker image used is defined by the version of Fabric in use. - -With Fabric v2.1.0 and later, the chaincode container can be configured to be started by different chaincode builders, and not the Peer. In this case, the environment used is not in the control of Fabric. +This table shows the summary of the compatibility of the Java chaincode libraries, together with the JVM version they require. -The Java libraries are produced are `group: 'org.hyperledger.fabric-chaincode-java', name: 'fabric-chaincode-shim'` +| Java chaincode version | Minimum supported Java | Java runtime | Docker image platforms | +|------------------------|------------------------|--------------|------------------------| +| v1.4 | 8 | 8 | amd64 | +| v2.2 | 11 | 11 | amd64 | +| v2.5.0 - v2.5.4 | 11 | 11 | amd64, arm64 | +| v2.5.5+ | 11 | 21 | amd64, arm64 | -### Supported JVMs +The Java runtime provided by the chaincode Docker image determines the maximum Java version (and features) that smart contract code can exploit when using the default Java chaincode container. -v1.4.x and v2.5.x Java Libraries are supported running in Java 11 with the x86_64 architecture. Later Java 11 versions are supported but are not tested. +Subject to a suitable runtime environment, the Java chaincode libraries can be used to communicate with Fabric peers at different LTS versions. The level of functionality is determined by the Fabric version in use and channel capabilities. -v1.4.x Java Libraries are supported running in Java 8 with the x86_64 architecture. Later Java 8 versions are supported but are not tested. +All Docker images, chaincode libraries and tools are tested using amd64 (x86-64) only. -Architecture Support: all docker images, JVMs, tools are tested under x86_64 ONLY +## Chaincode builder +The default Fabric chaincode builder creates a Docker container to run deployed smart contracts. Java chaincode Docker containers are built using the `hyperledger/fabric-javaenv` Docker image, tagged with the same major and minor version as the Fabric peer version. For example, Fabric v2.5 creates Java chaincode containers using the `hyperledger/fabric-javaenv:2.5` Docker image. Fabric v3 continues to use the v2.5 Java chaincode image. -### Default Peer Runtime selection +A different chaincode Docker image can be specified using the `CORE_CHAINCODE_JAVA_RUNTIME` environment variable on the Fabric peer. For example, `CORE_CHAINCODE_JAVA_RUNTIME=example/customJavaRuntime:latest`. -When using Fabric v2.5, the default docker image that is used to run the Java chaincode is *eclipse-temurin:11.0.21_9-jdk* +With Fabric v2 and later, an alternative chaincode builder can be configured on the Fabric peer. In this case the configured chaincode builder controls how chaincode is launched. See the [Fabric documentation](https://hyperledger-fabric.readthedocs.io/en/release-2.5/cc_launcher.html) for further details. -With the default docker image used by Fabric v2.5, if the packaged Java code contains a build script or a wrapper for either Maven or Gradle, it will be built using Gradle 7.0 wrapper, or Maven 3.8.1 wrapper. +## Chaincode packaging - - If both Gradle and Maven files are present Gradle is used. - - Gradle build files can be groovy, or kotlin. - - If the Gradle or Maven wrappers are present, this will used in preference to the installed wrappers. - -Remember that when using the wrappers, code will be downloaded from the internet. Keep this in mind for any installation with limited or no internet access. - -Alternatively it is recommended to package prebuilt jar files, including the contract and all dependencies, in which case no build or Internet access is required when installing Java chaincode. +When using the `hyperledger/fabric-javaenv` Java chaincode Docker images, deployed chaincode is built as follows: -Please check the [Dockerfile](./fabric-chaincode-docker/Dockerfile) that is used for the environment to see exactly how these versions are installed. +- If both Gradle and Maven files are present, Gradle is used. +- Gradle build files can be either Groovy or Kotlin. +- If Gradle or Maven wrappers are present, they will be used instead of the preinstalled Gradle or Maven versions. -### Supported Runtime communication with the Peer +Remember that when using the wrappers, code will be downloaded from the Internet. Keep this in mind for any installation with limited network access. -Subject to a suitable runtime environment, the 1.4 and 2.5 Java Libraries can used to communicate with Fabric Peers at 2.5 and previous LTS versions. The level of functionality that is implied by the Fabric version in use and channel capabilities. +Alternatively, it is recommended to package prebuilt JAR files, including the smart contract and all dependencies. In this case, no Internet access is required when deploying Java chaincode. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d872b5bc1..20f0f0d13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,29 +2,29 @@ We welcome contributions to the [Hyperledger Fabric](https://hyperledger-fabric.readthedocs.io) Project. There's always plenty to do! -If you have any questions about the project or how to contribute, you can find us in the [fabric-contracts-api](https://discordapp.com/channels/905194001349627914/943090527920877598) channel on Discord. +If you have any questions about the project or how to contribute, you can find us in the [fabric-contracts-api](https://discordapp.com/channels/905194001349627914/943090527920877598) channel on [Discord](https://discord.lfdecentralizedtrust.org/). Here are a few guidelines to help you contribute successfully... ## Issues -All issues are tracked in the issues tab in github. If you find a bug which we don't already know about, you can help us by creating a new issue describing the problem. Please include as much detail as possible to help us track down the cause.If you want to begin contributing code, looking through our open issues is a good way to start. Try looking for recent issues with detailed descriptions first, or ask us on Discord if you're unsure which issue to choose. +All issues are tracked in the issues tab in GitHub. If you find a bug which we don't already know about, you can help us by creating a new issue describing the problem. Please include as much detail as possible to help us track down the cause.If you want to begin contributing code, looking through our open issues is a good way to start. Try looking for recent issues with detailed descriptions first, or ask us on Discord if you're unsure which issue to choose. ## Enhancements -Make sure you have the support of the Hyperledger Fabric community before investing a lot of effort in project enhancements. Please look up the Fabric RFC process for large changes. +Make sure you have the support of the Hyperledger Fabric community before investing a lot of effort in project enhancements. Please look up the [Fabric RFC](https://github.com/hyperledger/fabric-rfcs) process for large changes. ## Pull Requests -We use our own forks and [Github Flow](https://guides.github.com/introduction/flow/index.html) to deliver changes to the code. Follow these steps to deliver your first pull request: +We use our own forks and [Github Flow](https://docs.github.com/en/get-started/using-github/github-flow) to deliver changes to the code. Follow these steps to deliver your first pull request: -1. [Fork the repository](https://guides.github.com/activities/forking/#fork) and create a new branch from `main`. +1. [Fork the repository](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project) and create a new branch from `main`. 2. If you've added code that should be tested, add tests! 3. If you've added any new features or made breaking changes, update the documentation. 4. Ensure all the tests pass. -5. Include the JIRA issue number, a descriptive message, and the [Developer Certificate of Origin (DCO) sign-off](https://github.com/probot/dco#how-it-works) on all commit messages. -6. [Issue a pull request](https://guides.github.com/activities/forking/#making-a-pull-request)! -7. [Azure DevOps](https://dev.azure.com/Hyperledger/Fabric-Chaincode-Java) builds must succeed before the pull request can be reviewed and merged. +5. Include the JIRA issue number, a descriptive message, and the [Developer Certificate of Origin (DCO) sign-off](https://github.com/dcoapp/app#how-it-works) on all commit messages. +6. [Issue a pull request](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#making-a-pull-request)! +7. [GitHub Actions](https://github.com/hyperledger/fabric-chaincode-java/actions) builds must succeed before the pull request can be reviewed and merged. ## Coding Style From 951e684fbc93e3919f6e0090d7c790eafba82e56 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Wed, 11 Dec 2024 18:44:20 +0000 Subject: [PATCH 04/12] Add an overview for the javaenv Docker image (#370) Signed-off-by: Mark S. Lewis --- fabric-chaincode-docker/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 fabric-chaincode-docker/README.md diff --git a/fabric-chaincode-docker/README.md b/fabric-chaincode-docker/README.md new file mode 100644 index 000000000..028052a04 --- /dev/null +++ b/fabric-chaincode-docker/README.md @@ -0,0 +1,16 @@ +# Quick reference + +- **Maintained by**: + [The Fabric Java chaincode maintainers](https://github.com/hyperledger/fabric-chaincode-java) + +# Overview + +This image is used by the default [Hyperledger Fabric](https://hyperledger-fabric.readthedocs.io/) chaincode builder when deploying Java smart contracts. It is not intended for use independently of Hyperledger Fabric. + +# Image variants + +Image tags correspond broadly to the major and minor version of Hyperledger Fabric for which they are primarily intended. More detailed information on image tags, interoperability, and supported Java versions can be found in the [compatibility](https://github.com/hyperledger/fabric-chaincode-java/blob/main/COMPATIBILITY.md) documentation. + +# License + +This image is provided under the [Apache-2.0](https://github.com/hyperledger/fabric-chaincode-java/blob/main/LICENSE) license. From 106cbd12c2148cc178ff9cdeb386b9462477bf56 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Fri, 13 Dec 2024 13:10:46 +0000 Subject: [PATCH 05/12] Update Docker image overview (#371) Simplify to text to avoid confusion on the relationship between image tags and Fabric versions. Signed-off-by: Mark S. Lewis --- fabric-chaincode-docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric-chaincode-docker/README.md b/fabric-chaincode-docker/README.md index 028052a04..4c87631da 100644 --- a/fabric-chaincode-docker/README.md +++ b/fabric-chaincode-docker/README.md @@ -9,7 +9,7 @@ This image is used by the default [Hyperledger Fabric](https://hyperledger-fabri # Image variants -Image tags correspond broadly to the major and minor version of Hyperledger Fabric for which they are primarily intended. More detailed information on image tags, interoperability, and supported Java versions can be found in the [compatibility](https://github.com/hyperledger/fabric-chaincode-java/blob/main/COMPATIBILITY.md) documentation. +Detailed information on image tags, interoperability, and supported Java versions can be found in the [compatibility](https://github.com/hyperledger/fabric-chaincode-java/blob/main/COMPATIBILITY.md) documentation. # License From bb141b3fd46eeaf029f114aba21eb04f48a4fb37 Mon Sep 17 00:00:00 2001 From: Dave Enyeart Date: Wed, 15 Jan 2025 16:24:30 -0500 Subject: [PATCH 06/12] Bump logback to 1.5.15 (#373) Signed-off-by: David Enyeart --- examples/fabric-contract-example-maven/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/fabric-contract-example-maven/pom.xml b/examples/fabric-contract-example-maven/pom.xml index b022d5aa4..df3438441 100644 --- a/examples/fabric-contract-example-maven/pom.xml +++ b/examples/fabric-contract-example-maven/pom.xml @@ -7,15 +7,15 @@ - 1.8 + 21 UTF-8 UTF-8 - 2.5.4 + 2.5.5 - 1.3.14 + 1.5.15 1.7.5 From 8fb3168255f307dcf0580338ccb546fd9d5feb65 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Sat, 22 Mar 2025 12:54:19 +0000 Subject: [PATCH 07/12] Use native runners to build Docker images (#374) Instead of relying on QEMU emulation to build multi-arch images, use native arm64 and amd64 build runners to build architecture-specific images, then publish multi-arch metadata with each of the image manifests. Signed-off-by: Mark S. Lewis --- .github/workflows/release.yml | 129 ++++++++++++++++++++++------------ .github/workflows/test.yml | 27 +++---- RELEASING.md | 9 ++- 3 files changed, 105 insertions(+), 60 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1a0c86938..3ed405376 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,14 +7,14 @@ name: Release on: push: tags: - - 'v[0-9]+.[0-9]+.[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+-*' + - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-*" workflow_dispatch: -jobs: - test: - uses: ./.github/workflows/test.yml +env: + IMAGE_NAME: ${{ github.repository_owner }}/fabric-javaenv +jobs: # Publishing steps to both the Github Packages and the Sonatype publishjars: strategy: @@ -24,7 +24,6 @@ jobs: - publishAllPublicationsToGithubPackagesRepository - publishAllPublicationsToReleaseRepository runs-on: ubuntu-latest - needs: test permissions: contents: read packages: write @@ -32,7 +31,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: - distribution: 'temurin' + distribution: "temurin" java-version: 21 - uses: gradle/actions/setup-gradle@v4 - name: Push to registry ${{ matrix.publish_target }} @@ -47,67 +46,107 @@ jobs: TARGET: ${{ matrix.publish_target }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Publish to Docker registries: docker.io and ghcr.io - publishdocker: - runs-on: ubuntu-latest - needs: test + docker-build-push: + name: Push Docker image + runs-on: ${{ matrix.arch.runner }} permissions: contents: read packages: write + strategy: + fail-fast: false + matrix: + arch: + - platform: linux-amd64 + runner: ubuntu-24.04 + - platform: linux-arm64 + runner: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: - distribution: 'temurin' + distribution: "temurin" java-version: 21 - uses: gradle/actions/setup-gradle@v4 - name: Build the dependencies needed for the image run: ./gradlew :fabric-chaincode-docker:copyAllDeps - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Get commit timestamp + run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> "${GITHUB_ENV}" + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: - buildkitd-flags: --debug - buildkitd-config-inline: | - [worker.oci] - max-parallelism = 1 + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub - # If testing on a fork, login error may occur and can be ignored - continue-on-error: true uses: docker/login-action@v3 with: + registry: docker.io username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to GitHub Container Registry + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build image + id: build + uses: docker/build-push-action@v6 + with: + file: fabric-chaincode-docker/Dockerfile + context: fabric-chaincode-docker + outputs: type=registry,"name=${{ format('ghcr.io/{0},docker.io/{0}', env.IMAGE_NAME) }}",push-by-digest=true,name-canonical=true + env: + SOURCE_DATE_EPOCH: ${{ env.SOURCE_DATE_EPOCH }} + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digest-${{ matrix.arch.platform }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + + docker-meta: + needs: docker-build-push + name: Publish Docker metadata + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + registry: + - docker.io + - ghcr.io + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digest-* + merge-multiple: true + - name: Login to ${{ matrix.registry }} uses: docker/login-action@v3 with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Docker meta + registry: ${{ matrix.registry }} + username: ${{ matrix.registry == 'docker.io' && secrets.DOCKERHUB_USERNAME || github.actor }} + password: ${{ matrix.registry == 'docker.io' && secrets.DOCKERHUB_TOKEN || secrets.GITHUB_TOKEN }} + - name: Docker metadata id: meta uses: docker/metadata-action@v5 with: - # If testing on a fork, Docker Hub publish might fail so place it last - images: | - ghcr.io/${{ github.repository_owner }}/fabric-javaenv - docker.io/${{ github.repository_owner }}/fabric-javaenv + images: ${{ matrix.registry }}/${{ env.IMAGE_NAME }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}.{{patch}} - - name: Get Git commit timestamps - run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV - - name: Build and push image - id: push - uses: docker/build-push-action@v6 - with: - platforms: linux/amd64,linux/arm64 - file: fabric-chaincode-docker/Dockerfile - context: fabric-chaincode-docker - tags: ${{ steps.meta.outputs.tags }} - push: ${{ github.event_name != 'pull_request' }} - labels: ${{ steps.meta.outputs.labels }} - env: - SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Create and push manifest list + working-directory: ${{ runner.temp }}/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("--tag " + .) | join(" ")' <<< "${DOCKER_METADATA_OUTPUT_JSON}") \ + $(printf '${{ matrix.registry }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + - name: Inspect image + run: docker buildx imagetools inspect '${{ matrix.registry }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 942cc24f0..9d45c2a4e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ on: workflow_call: inputs: ref: - default: '' + default: "" required: false type: string @@ -16,16 +16,16 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.ref }} - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - - uses: gradle/actions/setup-gradle@v4 - - name: Build and Unit test - run: ./gradlew :fabric-chaincode-shim:build + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + - name: Build and Unit test + run: ./gradlew :fabric-chaincode-shim:build intergationtest: runs-on: ubuntu-latest @@ -37,6 +37,9 @@ jobs: with: distribution: temurin java-version: 21 + - uses: actions/setup-node@v4 + with: + node-version: "lts/*" - uses: gradle/actions/setup-gradle@v4 - name: Populate chaincode with latest java-version run: | @@ -49,7 +52,7 @@ jobs: run: | curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh | bash -s -- binary npm install -g @hyperledger-labs/weft - + # set the path and cfg env var for the rest of the step echo "FABRIC_CFG_PATH=$GITHUB_WORKSPACE/config" >> $GITHUB_ENV echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH diff --git a/RELEASING.md b/RELEASING.md index 16288ffc0..7550061e5 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -3,9 +3,9 @@ The following artifacts are created as a result of releasing Fabric Chaincode Java: - docker images - - [fabric-javaenv](https://hub.docker.com/r/hyperledger/fabric-javaenv) + - [fabric-javaenv](https://hub.docker.com/r/hyperledger/fabric-javaenv) - Java libraries - - [fabric-chaincode-shim](https://search.maven.org/search?q=a:fabric-chaincode-shim) + - [fabric-chaincode-shim](https://search.maven.org/search?q=a:fabric-chaincode-shim) **Note:** A docker image with a matching V.R version is required before releasing a new version of Fabric. @@ -17,10 +17,12 @@ The following tasks are required before releasing: - Update version numbers in `build.gradle` files to the required version - Update test, sample, and docs files to match the new version -- Update the [`COMPATIBILITY.md`](./COMPATIBILITY.md) +- Update the [`COMPATIBILITY.md`](./COMPATIBILITY.md) See the [[FABCJ-289] release: 2.2.0 LTS](https://github.com/hyperledger/fabric-chaincode-java/pull/124) pull request for an example, although be careful to search for all versions in the codebase as they're easy to miss and things change! +Ensure the last branch build passed since exactly this repository state will be released. + ## Create release Creating a GitHub release on the [releases page](https://github.com/hyperledger/fabric-chaincode-java/releases) will trigger the build to publish the new release. @@ -57,6 +59,7 @@ See the [Bump version to 2.2.1](https://github.com/hyperledger/fabric-chaincode- ## Interim Build Publishing The nightly Azure Pipeline Builds will also publish the 'dev' drivers to Artifactory. These can be accessed via the repository at + ``` maven { url "https://hyperledger.jfrog.io/hyperledger/fabric-maven" From 48f96069ce69bfd5cefdf6e6e195594196b357a8 Mon Sep 17 00:00:00 2001 From: Muthu Date: Wed, 2 Apr 2025 00:37:20 +0530 Subject: [PATCH 08/12] Update to PMD 7.12.0 for static analysis (#375) Addresses CVE-2025-23215. Signed-off-by: Muthu Co-authored-by: Mark S. Lewis --- fabric-chaincode-shim/build.gradle | 2 +- .../fabric/contract/execution/ExecutionService.java | 1 + .../contract/execution/impl/ContractInvocationRequest.java | 2 +- .../fabric/contract/routing/impl/SerializerRegistryImpl.java | 4 ++-- .../main/java/org/hyperledger/fabric/ledger/Collection.java | 1 + .../fabric/shim/ext/sbe/impl/StateBasedEndorsementImpl.java | 2 +- .../hyperledger/fabric/shim/impl/InvocationTaskManager.java | 3 --- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/fabric-chaincode-shim/build.gradle b/fabric-chaincode-shim/build.gradle index eff78a88c..e5602c7cb 100644 --- a/fabric-chaincode-shim/build.gradle +++ b/fabric-chaincode-shim/build.gradle @@ -12,7 +12,7 @@ plugins { } pmd { - toolVersion = '7.7.0' + toolVersion = '7.12.0' ruleSetFiles = files('../pmd-ruleset.xml') ruleSets = [] // explicitly set to empty to avoid using the default configuration ignoreFailures = false diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/ExecutionService.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/ExecutionService.java index 8c8596808..629f21bb5 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/ExecutionService.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/ExecutionService.java @@ -15,6 +15,7 @@ * *

Service that executes {@link InvocationRequest} (wrapped Init/Invoke + extra data) using routing information */ +@FunctionalInterface public interface ExecutionService { /** diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/ContractInvocationRequest.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/ContractInvocationRequest.java index 7a3e0ff36..82a0f9967 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/ContractInvocationRequest.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/ContractInvocationRequest.java @@ -15,7 +15,7 @@ import org.hyperledger.fabric.shim.ChaincodeStub; public final class ContractInvocationRequest implements InvocationRequest { - @SuppressWarnings("PMD.ProperLogger") // PMD 7.7.0 gives a false positive here + @SuppressWarnings("PMD.ProperLogger") // PMD 7.12.0 gives a false positive here private static final Log LOGGER = LogFactory.getLog(ContractInvocationRequest.class); private static final Pattern NS_REGEX = Pattern.compile(":"); diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/SerializerRegistryImpl.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/SerializerRegistryImpl.java index 728966f09..99a41e03d 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/SerializerRegistryImpl.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/SerializerRegistryImpl.java @@ -24,7 +24,7 @@ public class SerializerRegistryImpl { private static final Logger LOGGER = Logger.getLogger(SerializerRegistryImpl.class); - private final Class annotationClass = Serializer.class; + private static final Class ANNOTATION_CLASS = Serializer.class; // Could index these by name and or type. private final Map contents = new HashMap<>(); @@ -68,7 +68,7 @@ public void findAndSetContents() throws InstantiationException, IllegalAccessExc try (ScanResult scanResult = classGraph.scan()) { for (final ClassInfo classInfo : - scanResult.getClassesWithAnnotation(this.annotationClass.getCanonicalName())) { + scanResult.getClassesWithAnnotation(this.ANNOTATION_CLASS.getCanonicalName())) { LOGGER.debug(() -> "Found class with contract annotation: " + classInfo.getName()); final Class cls = (Class) classInfo.loadClass(); LOGGER.debug("Loaded class"); diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/Collection.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/Collection.java index 1f109da8d..42a261074 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/Collection.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/Collection.java @@ -6,6 +6,7 @@ package org.hyperledger.fabric.ledger; /** Place holder. */ +@SuppressWarnings("PMD.ImplicitFunctionalInterface") public interface Collection { /** Constant that can be used to refer to the 'Worldstate' collection explicitly. */ diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementImpl.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementImpl.java index 1226a3cb7..56cae1a03 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementImpl.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementImpl.java @@ -23,7 +23,7 @@ /** Implements {@link StateBasedEndorsement}. */ public final class StateBasedEndorsementImpl implements StateBasedEndorsement { - @SuppressWarnings("PMD.ProperLogger") // PMD 7.7.0 reports a false positive + @SuppressWarnings("PMD.ProperLogger") // PMD 7.12.0 reports a false positive private static final Log LOGGER = LogFactory.getLog(StateBasedEndorsementImpl.class); private final Map orgs = new HashMap<>(); diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/InvocationTaskManager.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/InvocationTaskManager.java index aecc8e64d..bbed26d76 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/InvocationTaskManager.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/InvocationTaskManager.java @@ -183,9 +183,6 @@ private void processChaincodeMessage(final ChaincodeMessage chaincodeMessage) { case READY: handleMsg(chaincodeMessage, msgType); break; - default: - LOGGER.warning(() -> String.format(CANNOT_HANDLE_FORMAT, chaincodeMessage.getTxid(), msgType)); - break; } } From 13918c9be1dfcbb2118879f0a77cb8c28a4ea512 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Thu, 3 Apr 2025 18:51:40 +0100 Subject: [PATCH 09/12] Use OSV-Scanner v2 (#376) Update the vulnerability scan of dependencies to use OSV-Scanner v2. Also rework the Makefile targets so that a precompiled OSV-Scanner binary is downloaded and avoid the need for Go to be installed. Signed-off-by: Mark S. Lewis --- .github/workflows/scan.yml | 5 ----- Makefile | 31 +++++++++++++++++++++++++----- fabric-chaincode-shim/build.gradle | 10 ---------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 3bf42b58f..8b648f145 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -24,10 +24,5 @@ jobs: distribution: temurin java-version: 21 - uses: gradle/actions/setup-gradle@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: stable - cache: false - name: Scan run: make scan diff --git a/Makefile b/Makefile index f8f60fbfc..44118a94c 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,29 @@ # SPDX-License-Identifier: Apache-2.0 # -.PHONEY: scan -scan: - go install github.com/google/osv-scanner/cmd/osv-scanner@latest - ./gradlew --quiet resolveAndLockAll --write-locks - osv-scanner scan --lockfile=fabric-chaincode-shim/gradle.lockfile +bin_dir := bin +osv-scanner := $(bin_dir)/osv-scanner + +kernel_name := $(shell uname -s | tr '[:upper:]' '[:lower:]') +machine_hardware := $(shell uname -m) +ifeq ($(machine_hardware), x86_64) + machine_hardware := amd64 +endif +ifeq ($(machine_hardware), aarch64) + machine_hardware := arm64 +endif + +.PHONY: scan +scan: $(osv-scanner) + ./gradlew --quiet :fabric-chaincode-shim:dependencies --write-locks --configuration runtimeClasspath + bin/osv-scanner scan --lockfile=fabric-chaincode-shim/gradle.lockfile + +.PHONY: install-osv-scanner +install-osv-scanner: + mkdir -p '$(bin_dir)' + curl --fail --location --show-error --silent --output '$(osv-scanner)' \ + 'https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_$(kernel_name)_$(machine_hardware)' + chmod u+x '$(osv-scanner)' + +$(osv-scanner): + $(MAKE) install-osv-scanner diff --git a/fabric-chaincode-shim/build.gradle b/fabric-chaincode-shim/build.gradle index e5602c7cb..326225ea6 100644 --- a/fabric-chaincode-shim/build.gradle +++ b/fabric-chaincode-shim/build.gradle @@ -26,16 +26,6 @@ configurations { } } -tasks.register('resolveAndLockAll') { - notCompatibleWithConfigurationCache("Filters configurations at execution time") - doFirst { - assert gradle.startParameter.writeDependencyLocks : "$path must be run from the command line with the `--write-locks` flag" - } - doLast { - configurations.findAll { it.canBeResolved }.each { it.resolve() } - } -} - tasks.withType(org.gradle.api.tasks.testing.Test) { systemProperty 'CORE_CHAINCODE_LOGGING_LEVEL', 'DEBUG' } From 826f0779d1d8dc0a41b042f3346563bc8094c050 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Fri, 4 Apr 2025 15:15:52 +0100 Subject: [PATCH 10/12] Setup Go for vulnerability scan (#377) Go is still needed to run the vulnerability scan successfully on v2.5.5 and earlier. Signed-off-by: Mark S. Lewis --- .github/workflows/scan.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 8b648f145..b0754fd5d 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -19,6 +19,10 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} + - uses: actions/setup-go@v5 # Needed for scanning of v2.5.5 and earlier + with: + go-version: stable + cache: false - uses: actions/setup-java@v4 with: distribution: temurin From fd50d8a8cbfac8c2b45002ffe330dbbba5db6fd2 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Mon, 9 Jun 2025 21:24:18 +0100 Subject: [PATCH 11/12] Update dependencies (#379) - Update Gradle and Maven build tools in Docker image to latest versions. - Update project dependencies, including fabric-protos, which now requires protobuf-java v4 instead of v3. - Gradle build file modifications to avoid deprecated features. Signed-off-by: Mark S. Lewis --- build.gradle | 20 +++++----- .../Dockerfile | 8 ++-- .../build.gradle | 37 ++++++++----------- .../build.gradle.kts | 9 ++--- .../build.gradle | 36 +++++++----------- .../fabric-contract-example-maven/pom.xml | 12 ++---- examples/ledger-api/build.gradle | 32 ++++++---------- fabric-chaincode-docker/Dockerfile | 4 +- fabric-chaincode-docker/build.gradle | 36 ++++++++---------- .../build.gradle | 14 +++---- .../src/contracts/bare-gradle/build.gradle | 5 +-- .../contracts/fabric-ledger-api/build.gradle | 5 +-- .../contracts/fabric-shim-api/build.gradle | 9 ++--- fabric-chaincode-shim/build.gradle | 35 +++++++++--------- .../fabric/contract/metadata/TypeSchema.java | 2 +- .../shim/ChaincodeServerProperties.java | 2 +- .../impl/OpenTelemetryTracesProvider.java | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 18 files changed, 115 insertions(+), 157 deletions(-) diff --git a/build.gradle b/build.gradle index 97b795825..d0be9fd6c 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ */ plugins { - id "com.github.ben-manes.versions" version "0.51.0" - id "com.diffplug.spotless" version "6.25.0" + id "com.github.ben-manes.versions" version "0.52.0" + id "com.diffplug.spotless" version "7.0.4" } version = '2.5.6' @@ -28,15 +28,13 @@ allprojects { repositories { mavenCentral() - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } - maven { url "https://www.jitpack.io" } } spotless { format 'misc', { target '*.gradle', '.gitattributes', '.gitignore' trimTrailingWhitespace() - indentWithSpaces() + leadingTabsToSpaces() endWithNewline() } } @@ -62,14 +60,14 @@ subprojects { dependencies { implementation 'commons-cli:commons-cli:1.9.0' - implementation 'commons-logging:commons-logging:1.3.4' + implementation 'commons-logging:commons-logging:1.3.5' - testImplementation platform('org.junit:junit-bom:5.11.3') + testImplementation platform('org.junit:junit-bom:5.13.1') testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - testImplementation 'org.assertj:assertj-core:3.26.3' - testImplementation 'org.mockito:mockito-core:5.14.2' - testImplementation 'uk.org.webcompere:system-stubs-jupiter:2.1.7' + testImplementation 'org.assertj:assertj-core:3.27.3' + testImplementation 'org.mockito:mockito-core:5.18.0' + testImplementation 'uk.org.webcompere:system-stubs-jupiter:2.1.8' testImplementation 'org.hamcrest:hamcrest-library:3.0' } @@ -81,7 +79,7 @@ subprojects { spotless { java { removeUnusedImports() - palantirJavaFormat('2.50.0').formatJavadoc(true) + palantirJavaFormat('2.67.0').formatJavadoc(true) formatAnnotations() } } diff --git a/examples/fabric-contract-example-as-service/Dockerfile b/examples/fabric-contract-example-as-service/Dockerfile index 2afd44b6f..8e21e344a 100644 --- a/examples/fabric-contract-example-as-service/Dockerfile +++ b/examples/fabric-contract-example-as-service/Dockerfile @@ -4,7 +4,7 @@ # Example multi-stage dockerfile for Java Chaincode # the first stage -FROM gradle:jdk11 AS GRADLE_BUILD +FROM gradle:8-jdk21 AS gradle_build # copy the build.gradle and src code to the container COPY src/ src/ @@ -15,11 +15,11 @@ RUN gradle build shadowJar # the second stage of our build just needs the compiled files -FROM openjdk:11-jre-slim +FROM eclipse-temurin:21-jre # copy only the artifacts we need from the first stage and discard the rest -COPY --from=GRADLE_BUILD /home/gradle/build/libs/chaincode.jar /chaincode.jar +COPY --from=gradle_build /home/gradle/build/libs/chaincode.jar /chaincode.jar -ENV PORT 9999 +ENV PORT=9999 EXPOSE 9999 # set the startup command to execute the jar diff --git a/examples/fabric-contract-example-as-service/build.gradle b/examples/fabric-contract-example-as-service/build.gradle index b787d94b7..a8d365247 100644 --- a/examples/fabric-contract-example-as-service/build.gradle +++ b/examples/fabric-contract-example-as-service/build.gradle @@ -1,31 +1,24 @@ plugins { - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.6' id 'java' } -version '0.0.1' - -tasks.compileJava { - options.release.set(11) -} +version = '0.0.1' repositories { mavenCentral() maven { - url "https://www.jitpack.io" + url = "https://www.jitpack.io" } - maven { - url "https://hyperledger.jfrog.io/hyperledger/fabric-maven" - } - } dependencies { - compile 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.2' - compile 'org.json:json:20240303' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' - testImplementation 'org.assertj:assertj-core:3.26.3' - testImplementation 'org.mockito:mockito-core:5.13.0' + implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.5' + implementation 'org.json:json:20250517' + testImplementation 'org.junit.jupiter:junit-jupiter:5.13.1' + testImplementation 'org.assertj:assertj-core:3.27.3' + testImplementation 'org.mockito:mockito-core:5.18.0' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } shadowJar { @@ -39,14 +32,14 @@ shadowJar { } } +compileJava { + options.release.set(11) + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" +} + test { useJUnitPlatform() testLogging { - events "passed", "skipped", "failed" + events "PASSED", "SKIPPED", "FAILED" } } - - -tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" -} diff --git a/examples/fabric-contract-example-gradle-kotlin/build.gradle.kts b/examples/fabric-contract-example-gradle-kotlin/build.gradle.kts index 5cf57defb..92917bedc 100644 --- a/examples/fabric-contract-example-gradle-kotlin/build.gradle.kts +++ b/examples/fabric-contract-example-gradle-kotlin/build.gradle.kts @@ -14,11 +14,11 @@ plugins { version = "0.0.1" dependencies { - implementation("org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.2") - implementation("org.json:json:20240303") + implementation("org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.5") + implementation("org.json:json:20250517") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - testImplementation("org.junit.jupiter:junit-jupiter:5.11.0") + testImplementation("org.junit.jupiter:junit-jupiter:5.13.1") testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0") } @@ -27,9 +27,6 @@ repositories { maven { setUrl("https://jitpack.io") } - maven { - setUrl("https://hyperledger.jfrog.io/hyperledger/fabric-maven") - } } tasks { diff --git a/examples/fabric-contract-example-gradle/build.gradle b/examples/fabric-contract-example-gradle/build.gradle index ceb2a9a2a..a4b822229 100644 --- a/examples/fabric-contract-example-gradle/build.gradle +++ b/examples/fabric-contract-example-gradle/build.gradle @@ -1,31 +1,23 @@ plugins { - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.6' id 'java' } -version '0.0.1' - -tasks.compileJava { - options.release.set(11) -} +version = '0.0.1' repositories { mavenCentral() maven { - url "https://www.jitpack.io" + url = "https://www.jitpack.io" } - maven { - url "https://hyperledger.jfrog.io/hyperledger/fabric-maven" - } - } dependencies { - compile 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.2' - compile 'org.json:json:20240303' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' - testImplementation 'org.assertj:assertj-core:3.26.3' - testImplementation 'org.mockito:mockito-core:5.13.0' + compile 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.5' + compile 'org.json:json:20250517' + testImplementation 'org.junit.jupiter:junit-jupiter:5.13.1' + testImplementation 'org.assertj:assertj-core:3.27.3' + testImplementation 'org.mockito:mockito-core:5.18.0' } shadowJar { @@ -39,14 +31,14 @@ shadowJar { } } +compileJava { + options.release.set(11) + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" +} + test { useJUnitPlatform() testLogging { - events "passed", "skipped", "failed" + events "PASSED", "SKIPPED", "FAILED" } } - - -tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" -} diff --git a/examples/fabric-contract-example-maven/pom.xml b/examples/fabric-contract-example-maven/pom.xml index df3438441..41d2202fc 100644 --- a/examples/fabric-contract-example-maven/pom.xml +++ b/examples/fabric-contract-example-maven/pom.xml @@ -19,8 +19,8 @@ 1.7.5 - 5.3.0-RC1 - 1.3.0-RC1 + 5.13.1 + 1.13.1 @@ -29,10 +29,6 @@ jitpack.io https://www.jitpack.io - - artifactory - https://hyperledger.jfrog.io/hyperledger/fabric-maven - @@ -48,7 +44,7 @@ org.hyperledger.fabric fabric-protos - 0.3.3 + 0.3.7 compile @@ -92,7 +88,7 @@ org.mockito mockito-core - 2.10.0 + 5.18.0 diff --git a/examples/ledger-api/build.gradle b/examples/ledger-api/build.gradle index ceb2a9a2a..8f94a9b25 100644 --- a/examples/ledger-api/build.gradle +++ b/examples/ledger-api/build.gradle @@ -1,31 +1,23 @@ plugins { - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.6' id 'java' } version '0.0.1' -tasks.compileJava { - options.release.set(11) -} - repositories { mavenCentral() maven { url "https://www.jitpack.io" } - maven { - url "https://hyperledger.jfrog.io/hyperledger/fabric-maven" - } - } dependencies { - compile 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.2' - compile 'org.json:json:20240303' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' - testImplementation 'org.assertj:assertj-core:3.26.3' - testImplementation 'org.mockito:mockito-core:5.13.0' + compile 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.5' + compile 'org.json:json:20250517' + testImplementation 'org.junit.jupiter:junit-jupiter:5.13.1' + testImplementation 'org.assertj:assertj-core:3.27.3' + testImplementation 'org.mockito:mockito-core:5.18.0' } shadowJar { @@ -39,14 +31,14 @@ shadowJar { } } +compileJava { + options.release.set(11) + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" +} + test { useJUnitPlatform() testLogging { - events "passed", "skipped", "failed" + events "PASSED", "SKIPPED", "FAILED" } } - - -tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" -} diff --git a/fabric-chaincode-docker/Dockerfile b/fabric-chaincode-docker/Dockerfile index d8ae42298..eeaf43033 100644 --- a/fabric-chaincode-docker/Dockerfile +++ b/fabric-chaincode-docker/Dockerfile @@ -13,8 +13,8 @@ RUN curl -s "https://get.sdkman.io" | bash SHELL ["/bin/bash", "-c"] RUN source /root/.sdkman/bin/sdkman-init.sh \ - && sdk install gradle 8.11.1 \ - && sdk install maven 3.9.9 + && sdk install gradle 8.14.2 \ + && sdk install maven 3.9.10 FROM ${JAVA_IMAGE} AS dependencies diff --git a/fabric-chaincode-docker/build.gradle b/fabric-chaincode-docker/build.gradle index 324b3a7c4..b5dbbeb5c 100644 --- a/fabric-chaincode-docker/build.gradle +++ b/fabric-chaincode-docker/build.gradle @@ -4,47 +4,41 @@ * SPDX-License-Identifier: Apache-2.0 */ -buildscript { - repositories { - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } - maven { url "https://www.jitpack.io" } - mavenCentral() - gradlePluginPortal() - } - dependencies { - classpath 'com.bmuschko:gradle-docker-plugin:9.4.0' - } +plugins { + id 'com.bmuschko.docker-remote-api' version '9.4.0' } -apply plugin: 'com.bmuschko.docker-remote-api' +repositories { + mavenCentral() +} import com.bmuschko.gradle.docker.tasks.image.* -task copyLib (type: Copy) { +tasks.register('copyLib', Copy) { dependsOn ':fabric-chaincode-shim:build' from project(':fabric-chaincode-shim').configurations.runtimeClasspath into('build/distributions/chaincode-java/lib') } -task copyShimJar(type: Copy) { +tasks.register('copyShimJar', Copy) { dependsOn copyLib from project(':fabric-chaincode-shim').jar into('build/distributions/chaincode-java/lib') } -task copyStartScript(type: Copy) { +tasks.register('copyStartScript', Copy) { dependsOn copyShimJar - from ('start') - into ('build/distributions/chaincode-java') + from('start') + into('build/distributions/chaincode-java') } -task copyBuildScript(type: Copy) { +tasks.register('copyBuildScript', Copy) { dependsOn copyStartScript - from ('build.sh') - into ('build/distributions/chaincode-java') + from('build.sh') + into('build/distributions/chaincode-java') } -task copyAllDeps(type: Copy) { +tasks.register('copyAllDeps', Copy) { dependsOn copyBuildScript copy { from project(':fabric-chaincode-shim').getProjectDir() @@ -62,7 +56,7 @@ task copyAllDeps(type: Copy) { } } -task buildImage(type: DockerBuildImage) { +tasks.register('buildImage', DockerBuildImage) { dependsOn copyAllDeps inputDir = project.file('Dockerfile').parentFile images = ['hyperledger/fabric-javaenv', 'hyperledger/fabric-javaenv:2.5', 'hyperledger/fabric-javaenv:amd64-2.5.6', 'hyperledger/fabric-javaenv:amd64-latest'] diff --git a/fabric-chaincode-integration-test/build.gradle b/fabric-chaincode-integration-test/build.gradle index 9d2b1fc73..240c506e0 100644 --- a/fabric-chaincode-integration-test/build.gradle +++ b/fabric-chaincode-integration-test/build.gradle @@ -1,7 +1,7 @@ dependencies { implementation project(':fabric-chaincode-docker') implementation project(':fabric-chaincode-shim') - implementation 'org.json:json:20240303' + implementation 'org.json:json:20250517' } @@ -12,17 +12,17 @@ dependencies { // Show test results. testLogging { events "passed", "skipped", "failed" - showExceptions true - showCauses true - showStandardStreams true - exceptionFormat "full" + showExceptions = true + showCauses = true + showStandardStreams = true + exceptionFormat = "full" } } -task getLatestDockerImages{ +tasks.register('getLatestDockerImages') { doLast { - exec { + providers.exec { workingDir "." commandLine "sh", "-c", "./getDockerImages.sh" } diff --git a/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle b/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle index 5a82082e9..e7479c5cd 100644 --- a/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle +++ b/fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.gradleup.shadow' version '8.3.5' + id 'com.gradleup.shadow' version '8.3.6' id 'java' } @@ -17,7 +17,6 @@ compileJava { repositories { mavenCentral() - maven { url = "https://www.jitpack.io" } maven { url "$projectDir/repository" } @@ -25,7 +24,7 @@ repositories { dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.6' - implementation 'org.hyperledger.fabric:fabric-protos:0.3.3' + implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' } shadowJar { diff --git a/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle b/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle index 56c0f2512..97d990904 100644 --- a/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle +++ b/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.gradleup.shadow' version '8.3.5' + id 'com.gradleup.shadow' version '8.3.6' id 'java' } @@ -17,7 +17,6 @@ compileJava { repositories { mavenCentral() - maven { url = "https://www.jitpack.io" } maven { url "$projectDir/repository" } @@ -25,7 +24,7 @@ repositories { dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.6' - implementation 'org.hyperledger.fabric:fabric-protos:0.3.3' + implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' } shadowJar { diff --git a/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle b/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle index 06b4af177..b59cca3cc 100644 --- a/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle +++ b/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.gradleup.shadow' version '8.3.5' + id 'com.gradleup.shadow' version '8.3.6' id 'java' } @@ -17,7 +17,6 @@ compileJava { repositories { mavenCentral() - maven { url = "https://www.jitpack.io" } maven { url "$projectDir/repository" } @@ -25,9 +24,9 @@ repositories { dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.6' - implementation 'org.hyperledger.fabric:fabric-protos:0.3.3' - implementation 'commons-logging:commons-logging:1.2' - implementation 'com.google.code.gson:gson:2.10.1' + implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' + implementation 'commons-logging:commons-logging:1.3.5' + implementation 'com.google.code.gson:gson:2.13.1' } shadowJar { diff --git a/fabric-chaincode-shim/build.gradle b/fabric-chaincode-shim/build.gradle index 326225ea6..896f86c88 100644 --- a/fabric-chaincode-shim/build.gradle +++ b/fabric-chaincode-shim/build.gradle @@ -12,7 +12,7 @@ plugins { } pmd { - toolVersion = '7.12.0' + toolVersion = '7.14.0' ruleSetFiles = files('../pmd-ruleset.xml') ruleSets = [] // explicitly set to empty to avoid using the default configuration ignoreFailures = false @@ -31,33 +31,32 @@ tasks.withType(org.gradle.api.tasks.testing.Test) { } dependencies { - implementation platform('com.google.protobuf:protobuf-bom:3.25.5') - implementation platform('io.grpc:grpc-bom:1.68.1') - implementation platform('io.opentelemetry:opentelemetry-bom:1.42.1') + implementation platform('com.google.protobuf:protobuf-bom:4.31.1') + implementation platform('io.grpc:grpc-bom:1.73.0') + implementation platform('io.opentelemetry:opentelemetry-bom:1.51.0') - implementation 'org.hyperledger.fabric:fabric-protos:0.3.3' - implementation 'org.bouncycastle:bcpkix-jdk18on:1.79' - implementation 'org.bouncycastle:bcprov-jdk18on:1.79' + implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' + implementation 'org.bouncycastle:bcpkix-jdk18on:1.81' + implementation 'org.bouncycastle:bcprov-jdk18on:1.81' implementation 'io.github.classgraph:classgraph:4.8.179' - implementation 'com.github.everit-org.json-schema:org.everit.json.schema:1.14.4' - implementation 'org.json:json:20240303' + implementation 'com.github.erosb:everit-json-schema:1.14.6' + implementation 'org.json:json:20250517' implementation 'com.google.protobuf:protobuf-java-util' implementation 'io.grpc:grpc-netty-shaded' implementation 'io.grpc:grpc-protobuf' implementation 'io.grpc:grpc-stub' testImplementation 'io.grpc:grpc-inprocess' - // Required if using Java 11+ as no longer bundled in the core libraries - testImplementation 'javax.xml.bind:jaxb-api:2.3.1' implementation 'io.opentelemetry:opentelemetry-api' - implementation 'io.opentelemetry.proto:opentelemetry-proto:1.3.2-alpha' + implementation 'io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha' implementation 'io.opentelemetry:opentelemetry-sdk' implementation 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure' implementation 'io.opentelemetry:opentelemetry-sdk-trace' implementation 'io.opentelemetry:opentelemetry-exporter-otlp' implementation 'io.opentelemetry:opentelemetry-extension-trace-propagators' - implementation 'io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.8.0-alpha' + implementation 'io.opentelemetry.semconv:opentelemetry-semconv:1.32.0' + implementation 'io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.16.0-alpha' } sourceSets { @@ -343,10 +342,10 @@ tasks.withType(Test) { TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.STANDARD_OUT - exceptionFormat TestExceptionFormat.FULL - showExceptions true - showCauses true - showStackTraces true + exceptionFormat = TestExceptionFormat.FULL + showExceptions = true + showCauses = true + showStackTraces = true // set options for log level DEBUG and INFO debug { @@ -356,7 +355,7 @@ tasks.withType(Test) { TestLogEvent.SKIPPED, TestLogEvent.STANDARD_ERROR, TestLogEvent.STANDARD_OUT - exceptionFormat TestExceptionFormat.FULL + exceptionFormat = TestExceptionFormat.FULL } info.events = debug.events info.exceptionFormat = debug.exceptionFormat diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/TypeSchema.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/TypeSchema.java index 8b11eccd9..dab261e54 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/TypeSchema.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/TypeSchema.java @@ -25,7 +25,7 @@ * *

Does not include the "schema" top level map */ -@SuppressWarnings({"PMD.LooseCoupling", "PMD.GodClass"}) +@SuppressWarnings("PMD.GodClass") public final class TypeSchema extends HashMap { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(TypeSchema.class.getName()); diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeServerProperties.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeServerProperties.java index 4352fe14a..8197be59e 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeServerProperties.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeServerProperties.java @@ -314,7 +314,7 @@ public void setTlsEnabled(final boolean tlsEnabled) { * * @throws IllegalArgumentException if any properties are not valid. */ - @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + @SuppressWarnings("PMD.CyclomaticComplexity") public void validate() { if (this.getServerAddress() == null) { throw new IllegalArgumentException("chaincodeServerProperties.getServerAddress() must be set"); diff --git a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/OpenTelemetryTracesProvider.java b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/OpenTelemetryTracesProvider.java index 54a3034f0..4801ebe33 100644 --- a/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/OpenTelemetryTracesProvider.java +++ b/fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/OpenTelemetryTracesProvider.java @@ -13,7 +13,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; -import io.opentelemetry.semconv.ResourceAttributes; +import io.opentelemetry.semconv.ServiceAttributes; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -32,7 +32,7 @@ public final class OpenTelemetryTracesProvider implements TracesProvider { @Override public void initialize(final Properties props) { String serviceName = props.getProperty(CORE_CHAINCODE_ID_NAME, "unknown"); - props.setProperty(ResourceAttributes.SERVICE_NAME.getKey(), serviceName); + props.setProperty(ServiceAttributes.SERVICE_NAME.getKey(), serviceName); OpenTelemetry openTelemetry = AutoConfiguredOpenTelemetrySdk.builder() .addPropertiesSupplier(() -> getOpenTelemetryProperties(props)) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c820..ff23a68d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 1056635aa8e31ea3e29b1e54b841e59571bb370a Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Fri, 20 Jun 2025 16:33:44 +0100 Subject: [PATCH 12/12] Use JReleaser to publish to new Maven Central API (#380) Signed-off-by: Mark S. Lewis --- .github/workflows/release.yml | 42 +++++--- fabric-chaincode-shim/build.gradle | 159 +++++++++++++++-------------- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 43764 bytes gradlew | 9 +- gradlew.bat | 4 +- 5 files changed, 114 insertions(+), 100 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ed405376..76b72b2d5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,14 +15,7 @@ env: IMAGE_NAME: ${{ github.repository_owner }}/fabric-javaenv jobs: - # Publishing steps to both the Github Packages and the Sonatype - publishjars: - strategy: - fail-fast: false - matrix: - publish_target: - - publishAllPublicationsToGithubPackagesRepository - - publishAllPublicationsToReleaseRepository + publish-github: runs-on: ubuntu-latest permissions: contents: read @@ -34,18 +27,35 @@ jobs: distribution: "temurin" java-version: 21 - uses: gradle/actions/setup-gradle@v4 - - name: Push to registry ${{ matrix.publish_target }} + - name: Publish to GitHub Packages run: | - set -xev - ./gradlew -Psigning.key="${SIGNING_KEY}" -Psigning.password="${SIGNING_PASSWORD}" -PossrhUsername="${OSSRH_USER}" -PossrhPassword="${OSSRH_PASSWORD}" ${TARGET} + ./gradlew publishAllPublicationsToGitHubRepository env: - SIGNING_PASSWORD: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} - SIGNING_KEY: ${{ secrets.OSSRH_GPG_SECRET_KEY }} - OSSRH_USER: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - TARGET: ${{ matrix.publish_target }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + publish-maven: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + - name: Publish to Maven Central + run: | + ./gradlew publishAllPublicationsToStagingRepository + ./gradlew jreleaserDeploy --stacktrace + env: + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} + JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.MAVENCENTRAL_USERNAME }} + JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.MAVENCENTRAL_PASSWORD }} + docker-build-push: name: Push Docker image runs-on: ${{ matrix.arch.runner }} diff --git a/fabric-chaincode-shim/build.gradle b/fabric-chaincode-shim/build.gradle index 896f86c88..8cff266f8 100644 --- a/fabric-chaincode-shim/build.gradle +++ b/fabric-chaincode-shim/build.gradle @@ -6,8 +6,9 @@ plugins { id 'maven-publish' - id 'jacoco' id 'signing' + id 'org.jreleaser' version '1.18.0' + id 'jacoco' id 'pmd' } @@ -137,7 +138,7 @@ jacocoTestCoverageVerification { test.finalizedBy(jacocoTestReport) test.finalizedBy(jacocoTestCoverageVerification) -task licenseCheck { +tasks.register('licenseCheck') { group = "license" description = "Checks the License part of each source file" @@ -146,34 +147,37 @@ task licenseCheck { def missing = new LinkedList() sourceSets.forEach { sourceSet -> - sourceSet.allSource.findAll { !it.path.contains("build") && !(it.path.contains("test") && it.path.contains("resources"))}.each { + sourceSet.allSource.findAll { + !it.path.contains("build") && + !(it.path.contains("test") && it.path.contains("resources")) + }.each { file -> - if (!file.name.contains("json")){ - BufferedReader r = new BufferedReader(new FileReader(file)) - def line, hasSPDX = false, hasTraditional = false - while ((line = r.readLine()) != null) { - if (line.contains("SPDX-License-Identifier")) { - hasSPDX = true - break + if (!file.name.contains("json")) { + BufferedReader r = new BufferedReader(new FileReader(file)) + def line, hasSPDX = false, hasTraditional = false + while ((line = r.readLine()) != null) { + if (line.contains("SPDX-License-Identifier")) { + hasSPDX = true + break + } + if (line.contains("http://www.apache.org/licenses/LICENSE-2.0")) { + hasTraditional = true + break + } } - if (line.contains("http://www.apache.org/licenses/LICENSE-2.0")) { - hasTraditional = true - break - } - } - if (!hasSPDX) { - if (hasTraditional) { - noSPDX.add(file) - } else { - missing.add(file) + if (!hasSPDX) { + if (hasTraditional) { + noSPDX.add(file) + } else { + missing.add(file) + } } } - } } } if (noSPDX.isEmpty()) { - println "All remaining files have Apache 2.0 headers" + println "All remaining files have Apache 2.0 headers." } else { println "We are standardizing with the SPDX style license headers." println "The following files contain the traditional license headers which are still valid:" @@ -212,17 +216,13 @@ javadoc { classpath = sourceSets.main.runtimeClasspath - javadoc.options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('Xwerror', '-quiet') options.overview = "src/main/java/org/hyperledger/fabric/overview.html" -} -if (JavaVersion.current().isJava8Compatible()) { - project.tasks.withType(Javadoc) { - options.addStringOption('Xdoclint:all', '-quiet') - options.addStringOption('Xwerror', '-quiet') - } } +def final stagingDeployUrl = layout.buildDirectory.dir('staging-deploy') publishing { publications { @@ -235,10 +235,12 @@ publishing { name = 'JavaChaincodeShim' packaging = 'jar' description = 'Hyperledger Fabric Java Chaincode Shim' - url = 'http://www.hyperledger.org/' + url = 'https://hyperledger.github.io/fabric-chaincode-java/' scm { - url = 'https://github.com/hyperledger/fabric-chaincode-java.git' + connection = 'scm:git:https://github.com/hyperledger/fabric-chaincode-java.git' + developerConnection = 'scm:git:ssh://github.com:hyperledger/fabric-chaincode-java.git' + url = 'https://github.com/hyperledger/fabric-chaincode-java' } licenses { license { @@ -248,26 +250,16 @@ publishing { } developers { - developer { - id = 'gennadylaventman' - name = 'Gennady Laventman' - email = 'gennady@il.ibm.com' - } - developer { - id = 'luiss' - name = 'Luis Sanchez' - email = 'luiss@me.com' - } - developer { - id = 'C0rWin' - name = 'Artem Barger' - email = 'bartem@il.ibm.com' - } developer { id = 'denyeart' name = 'David Enyeart' email = 'enyeart@us.ibm.com' } + developer { + id = 'bestbeforetoday' + name = 'Mark S. Lewis' + email = 'Mark.S.Lewis@outlook.com' + } } } } @@ -276,17 +268,12 @@ publishing { repositories { maven { - name = "release" - url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - credentials { - username = project.findProperty('ossrhUsername') - password = project.findProperty('ossrhPassword') - } - + name = "Staging" + url = stagingDeployUrl } maven { - name = "GitHubPackages" + name = "GitHub" url = "https://maven.pkg.github.com/hyperledger/fabric-chaincode-java" credentials { username = System.getenv("GITHUB_ACTOR") @@ -297,16 +284,33 @@ publishing { } signing { - println "Signing" - if (project.findProperty('signing.key')) { - def signingKey = project.findProperty('signing.key') - def signingPassword = project.findProperty('signing.password') - useInMemoryPgpKeys(signingKey, signingPassword) - - sign publishing.publications.shim - println "... signed" - } else { - println "... no keys to use " + required = { gradle.taskGraph.hasTask(":${project.name}:publishShimPublicationToStagingRepository") } + + def signingKey = findProperty('signingKey') + def signingPassword = findProperty('signingPassword') + useInMemoryPgpKeys(signingKey, signingPassword) + + sign publishing.publications.shim +} + +jreleaser { + gitRootSearch = true + deploy { + maven { + mavenCentral { + sonatype { + active = 'ALWAYS' + url = 'https://central.sonatype.com/api/v1/publisher' + sign = false + stagingRepository(file(stagingDeployUrl).toString()) + } + } + } + } + release { + github { + enabled = false + } } } @@ -314,8 +318,8 @@ signing { // otherwise this fails with a duplicates error. // (see https://github.com/gradle/gradle/issues/17236) -task sourcesJar(type: Jar) { - duplicatesStrategy = 'include' +tasks.register('sourcesJar', Jar) { + duplicatesStrategy = DuplicatesStrategy.INCLUDE archiveClassifier = 'sources' from sourceSets.main.allSource } @@ -332,16 +336,16 @@ build.dependsOn licenseCheck import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent -tasks.withType(Test) { +tasks.withType(Test).configureEach { environment "CORE_PEER_LOCALMSPID", "mymsp" testLogging { // set options for log level LIFECYCLE events TestLogEvent.FAILED, - TestLogEvent.PASSED, - TestLogEvent.SKIPPED, - TestLogEvent.STANDARD_OUT + TestLogEvent.PASSED, + TestLogEvent.SKIPPED, + TestLogEvent.STANDARD_OUT exceptionFormat = TestExceptionFormat.FULL showExceptions = true showCauses = true @@ -350,22 +354,23 @@ tasks.withType(Test) { // set options for log level DEBUG and INFO debug { events TestLogEvent.STARTED, - TestLogEvent.FAILED, - TestLogEvent.PASSED, - TestLogEvent.SKIPPED, - TestLogEvent.STANDARD_ERROR, - TestLogEvent.STANDARD_OUT + TestLogEvent.FAILED, + TestLogEvent.PASSED, + TestLogEvent.SKIPPED, + TestLogEvent.STANDARD_ERROR, + TestLogEvent.STANDARD_OUT exceptionFormat = TestExceptionFormat.FULL } info.events = debug.events info.exceptionFormat = debug.exceptionFormat - afterSuite { desc, result -> + afterSuite {desc, result -> if (!desc.parent) { // will match the outermost suite def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" def startItem = '| ', endItem = ' |' def repeatLength = startItem.length() + output.length() + endItem.length() - println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) + println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + + ('-' * repeatLength)) } } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 34943 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

Gn4F+B~IivciU9^t0j-Mgao3 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 DDw4oP diff --git a/gradlew b/gradlew index f5feea6d6..23d15a936 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -115,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -206,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9b42019c7..5eed7ee84 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell