diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml index ee08b11a5..ed3adb48b 100644 --- a/.github/workflows/e2e-tests.yaml +++ b/.github/workflows/e2e-tests.yaml @@ -45,7 +45,7 @@ jobs: uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: "temurin" - java-version: "21.0.4" + java-version: "21.0.5" - name: Run Acceptance Tests id: acceptance-tests diff --git a/.github/workflows/pr-checks.yaml b/.github/workflows/pr-checks.yaml index 6a1cf78b0..2fe5706e9 100644 --- a/.github/workflows/pr-checks.yaml +++ b/.github/workflows/pr-checks.yaml @@ -17,9 +17,27 @@ env: GRADLE_EXEC: ./gradlew permissions: + id-token: write contents: read + packages: write jobs: + check-gradle: + name: Gradle + uses: ./.github/workflows/zxc-verify-gradle-build-determinism.yaml + with: + ref: ${{ github.event.inputs.ref || '' }} + java-distribution: ${{ inputs.java-distribution || 'temurin' }} + java-version: ${{ inputs.java-version || '21.0.5' }} + + check-docker: + name: Docker + uses: ./.github/workflows/zxc-verify-docker-build-determinism.yaml + with: + ref: ${{ github.event.inputs.ref || '' }} + java-distribution: ${{ inputs.java-distribution || 'temurin' }} + java-version: ${{ inputs.java-version || '21.0.5' }} + compile: name: "Gradle Checks" runs-on: block-node-linux-medium @@ -46,7 +64,7 @@ jobs: uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: "temurin" - java-version: "21.0.4" + java-version: "21.0.5" - name: Cache Gradle packages uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 diff --git a/.github/workflows/release-automation.yaml b/.github/workflows/release-automation.yaml index 311eb1108..d835d3329 100644 --- a/.github/workflows/release-automation.yaml +++ b/.github/workflows/release-automation.yaml @@ -80,7 +80,7 @@ jobs: uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: "temurin" - java-version: "21.0.4" + java-version: "21.0.5" - name: Setup Gradle uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 @@ -181,7 +181,7 @@ jobs: uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: "temurin" - java-version: "21.0.4" + java-version: "21.0.5" - name: Setup Gradle uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 diff --git a/.github/workflows/release-push-image.yaml b/.github/workflows/release-push-image.yaml index a6e2478b8..464e7a30c 100644 --- a/.github/workflows/release-push-image.yaml +++ b/.github/workflows/release-push-image.yaml @@ -20,6 +20,7 @@ defaults: shell: bash permissions: + id-token: write contents: read packages: write @@ -29,23 +30,24 @@ env: REGISTRY: ghcr.io jobs: -# check-gradle: -# name: Gradle -# uses: ./.github/workflows/zxc-verify-gradle-build-determinism.yaml -# with: -# ref: ${{ github.event.inputs.ref || '' }} -# java-distribution: ${{ inputs.java-distribution || 'temurin' }} -# java-version: ${{ inputs.java-version || '21.0.4' }} - -# check-docker: -# name: Docker -# uses: ./.github/workflows/zxc-verify-docker-build-determinism.yaml -# with: -# ref: ${{ github.event.inputs.ref || '' }} -# java-distribution: ${{ inputs.java-distribution || 'temurin' }} -# java-version: ${{ inputs.java-version || '21.0.4' }} + check-gradle: + name: Gradle + uses: ./.github/workflows/zxc-verify-gradle-build-determinism.yaml + with: + ref: ${{ github.event.inputs.ref || '' }} + java-distribution: ${{ inputs.java-distribution || 'temurin' }} + java-version: ${{ inputs.java-version || '21.0.5' }} + + check-docker: + name: Docker + uses: ./.github/workflows/zxc-verify-docker-build-determinism.yaml + with: + ref: ${{ github.event.inputs.ref || '' }} + java-distribution: ${{ inputs.java-distribution || 'temurin' }} + java-version: ${{ inputs.java-version || '21.0.5' }} publish: + needs: [check-gradle, check-docker] runs-on: block-node-linux-medium steps: @@ -56,12 +58,14 @@ jobs: - name: Checkout repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 - name: Install JDK uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: "temurin" - java-version: "21.0.4" + java-version: "21.0.5" - name: Build run: ./gradlew clean build @@ -80,6 +84,9 @@ jobs: uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 with: driver-opts: network=host + buildkitd-config-inline: | + [registry."docker.io"] + mirrors = ["https://hub.mirror.docker.lat.ope.eng.hashgraph.io"] - name: Extract version id: extract_version @@ -87,6 +94,12 @@ jobs: VERSION=$(cat version.txt) echo "VERSION=${VERSION}" >> $GITHUB_ENV + - name: Extract Source Date Epoch + id: extract_source_date_epoch + run: | + SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) + echo "SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}" >> $GITHUB_ENV + - name: Server - Build and push image uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0 with: @@ -99,6 +112,7 @@ jobs: tags: ${{ env.REGISTRY }}/${{ github.repository }}:${{ env.VERSION }} build-args: | VERSION=${{ env.VERSION }} + SOURCE_DATE_EPOCH=${{ env.SOURCE_DATE_EPOCH }} build-contexts: | distributions=./server/build/distributions diff --git a/.github/workflows/smoke-test.yaml b/.github/workflows/smoke-test.yaml index 7519ba7d3..e89dd9523 100644 --- a/.github/workflows/smoke-test.yaml +++ b/.github/workflows/smoke-test.yaml @@ -46,7 +46,7 @@ jobs: uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: "temurin" - java-version: "21.0.4" + java-version: "21.0.5" - name: Install grpcurl run: | diff --git a/.github/workflows/support/scripts/generate-docker-artifact-baseline.sh b/.github/workflows/support/scripts/generate-docker-artifact-baseline.sh index b40966c62..18924bad0 100755 --- a/.github/workflows/support/scripts/generate-docker-artifact-baseline.sh +++ b/.github/workflows/support/scripts/generate-docker-artifact-baseline.sh @@ -2,7 +2,7 @@ set -o pipefail set +e -readonly DOCKER_IMAGE_NAME="server" +readonly DOCKER_IMAGE_NAME="hashgraph/hedera-block-node" GROUP_ACTIVE="false" @@ -62,139 +62,142 @@ start_group "Configuring Environment" trap 'rm -rf "${TEMP_DIR}"' EXIT end_task "DONE (Path: ${TEMP_DIR})" -# start_task "Resolving the GITHUB_WORKSPACE path" -# # Ensure GITHUB_WORKSPACE is provided or default to the repository root -# if [[ -z "${GITHUB_WORKSPACE}" || ! -d "${GITHUB_WORKSPACE}" ]]; then -# GITHUB_WORKSPACE="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)" -# fi -# end_task "DONE (Path: ${GITHUB_WORKSPACE})" -# -# start_task "Resolving the GITHUB_OUTPUT path" -# # Ensure GITHUB_OUTPUT is provided or default to the repository root -# if [[ -z "${GITHUB_OUTPUT}" ]]; then -# GITHUB_OUTPUT="${TEMP_DIR}/workflow-output.txt" -# fi -# end_task "DONE (Path: ${GITHUB_OUTPUT})" -# -# start_task "Resolving the GITHUB_SHA hash" -# if [[ -z "${GITHUB_SHA}" ]]; then -# GITHUB_SHA="$(git rev-parse HEAD | tr -d '[:space:]')" || fail "ERROR (Exit Code: ${?})" "${?}" -# fi -# end_task "DONE (Commit: ${GITHUB_SHA})" -# -# start_task "Resolving the MANIFEST_PATH variable" -# if [[ -z "${MANIFEST_PATH}" ]]; then -# MANIFEST_PATH="${GITHUB_WORKSPACE}/.manifests/gradle" -# fi -# end_task "DONE (Path: ${MANIFEST_PATH})" -# -# start_task "Ensuring the MANIFEST_PATH location is present" -# if [[ ! -d "${MANIFEST_PATH}" ]]; then -# mkdir -p "${MANIFEST_PATH}" || fail "ERROR (Exit Code: ${?})" "${?}" -# fi -# end_task -# -# start_task "Resolving the SKOPEO_VERSION variable" -# if [[ -z "${SKOPEO_VERSION}" ]]; then -# SKOPEO_VERSION="v1.14.0" -# fi -# end_task "DONE (Version: ${SKOPEO_VERSION})" -# -# start_task "Resolving the SKOPEO_IMAGE_NAME variable" -# if [[ -z "${SKOPEO_IMAGE_NAME}" ]]; then -# SKOPEO_IMAGE_NAME="quay.io/skopeo/stable:${SKOPEO_VERSION}" -# fi -# end_task "DONE (Image Name: ${SKOPEO_IMAGE_NAME})" -# -# start_task "Checking for the DOCKER command" -# if command -v docker >/dev/null 2>&1; then -# DOCKER="$(command -v docker)" || fail "ERROR (Exit Code: ${?})" "${?}" -# export DOCKER -# else -# fail "ERROR (Exit Code: ${?})" "${?}" -# fi -# end_task "DONE (Found: ${DOCKER})" -# -# start_task "Resolving the Docker Client Configuration" -# SKOPEO_BIND_MOUNT="" -# SKOPEO_CREDENTIAL_OPTS="" -# DOCKER_CONFIG_DIR="${HOME}/.docker" -# if [[ -d "${DOCKER_CONFIG_DIR}" ]]; then -# SKOPEO_BIND_MOUNT="--volume ${DOCKER_CONFIG_DIR}:/tmp/docker" -# SKOPEO_CREDENTIAL_OPTS="--authfile /tmp/docker/config.json" -# fi -# export SKOPEO_BIND_MOUNT SKOPEO_CREDENTIAL_OPTS -# end_task "DONE" -# -# start_task "Checking for the SKOPEO command" -# if command -v skopeo >/dev/null 2>&1; then -# SKOPEO="$(command -v skopeo)" || fail "ERROR (Exit Code: ${?})" "${?}" -# export SKOPEO -# else -# ${DOCKER} pull "${SKOPEO_IMAGE_NAME}" >/dev/null 2>&1 || fail "ERROR (Exit Code: ${?})" "${?}" -# SKOPEO="${DOCKER} run ${SKOPEO_BIND_MOUNT} --rm --network host ${SKOPEO_IMAGE_NAME}" -# export SKOPEO -# fi -# end_task "DONE (Found: ${SKOPEO})" -# -# start_task "Checking for the JQ command" -# if command -v jq >/dev/null 2>&1; then -# JQ="$(command -v jq)" || fail "ERROR (Exit Code: ${?})" "${?}" -# export JQ -# else -# fail "ERROR (Exit Code: ${?})" "${?}" -# fi -# end_task "DONE (Found: ${JQ})" -#end_group -# -#start_group "Prepare the Docker Image Information" -# start_task "Resolving the DOCKER_REGISTRY variable" -# if [[ -z "${DOCKER_REGISTRY}" ]]; then -# DOCKER_REGISTRY="localhost:5000" -# fi -# end_task "DONE (Registry: ${DOCKER_REGISTRY})" -# -# start_task "Resolving the DOCKER_TAG variable" -# if [[ -z "${DOCKER_TAG}" ]]; then -# DOCKER_TAG="$(echo "${GITHUB_SHA}" | tr -d '[:space:]' | cut -c1-8)" -# fi -# end_task "DONE (Tag: ${DOCKER_TAG})" -# -# start_task "Resolving the Fully Qualified Image Name" -# FQ_IMAGE_NAME="${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_TAG}" -# end_task "DONE (Image: ${FQ_IMAGE_NAME})" -#end_group -# -#start_group "Generate Docker Image Manifest (linux/amd64)" -# ${SKOPEO} --override-os linux --override-arch amd64 inspect ${SKOPEO_CREDENTIAL_OPTS} --tls-verify=false "docker://${FQ_IMAGE_NAME}" | tee "${TEMP_DIR}/linux-amd64.manifest.json" || fail "SKOPEO ERROR (Exit Code: ${?})" "${?}" -# ${JQ} -r '.Layers[]' "${TEMP_DIR}/linux-amd64.manifest.json" | tee "${TEMP_DIR}/linux-amd64.layers.json" >/dev/null 2>&1 || fail "JQ LAYER ERROR (Exit Code: ${?})" "${?}" -# ${JQ} -r 'del(.RepoTags) | del(.LayersData) | del(.Digest) | del(.Name)' "${TEMP_DIR}/linux-amd64.manifest.json" | tee "${TEMP_DIR}/linux-amd64.comparable.json" >/dev/null 2>&1 || fail "JQ COMP ERROR (Exit Code: ${?})" "${?}" -#end_group -# -#start_group "Generate Docker Image Manifest (linux/arm64)" -# ${SKOPEO} --override-os linux --override-arch arm64 inspect ${SKOPEO_CREDENTIAL_OPTS} --tls-verify=false "docker://${FQ_IMAGE_NAME}" | tee "${TEMP_DIR}/linux-arm64.manifest.json" || fail "SKOPEO ERROR (Exit Code: ${?})" "${?}" -# ${JQ} -r '.Layers[]' "${TEMP_DIR}/linux-arm64.manifest.json" | tee "${TEMP_DIR}/linux-arm64.layers.json" >/dev/null 2>&1 || fail "JQ LAYER ERROR (Exit Code: ${?})" "${?}" -# ${JQ} -r 'del(.RepoTags) | del(.LayersData) | del(.Digest) | del(.Name)' "${TEMP_DIR}/linux-arm64.manifest.json" | tee "${TEMP_DIR}/linux-arm64.comparable.json" >/dev/null 2>&1 || fail "JQ COMP ERROR (Exit Code: ${?})" "${?}" -#end_group -# -#start_group "Generating Final Release Manifests" -# -# start_task "Generating the manifest archive" -# MANIFEST_FILES=("linux-amd64.manifest.json" "linux-amd64.layers.json" "linux-amd64.comparable.json") -# MANIFEST_FILES+=("linux-arm64.manifest.json" "linux-arm64.layers.json" "linux-arm64.comparable.json") -# tar -czf "${TEMP_DIR}/manifest.tar.gz" -C "${TEMP_DIR}" "${MANIFEST_FILES[@]}" >/dev/null 2>&1 || fail "TAR ERROR (Exit Code: ${?})" "${?}" -# end_task -# -# start_task "Copying the manifest files" -# cp "${TEMP_DIR}/manifest.tar.gz" "${MANIFEST_PATH}/${GITHUB_SHA}.tar.gz" || fail "COPY ERROR (Exit Code: ${?})" "${?}" -# cp "${TEMP_DIR}"/*.json "${MANIFEST_PATH}/" || fail "COPY ERROR (Exit Code: ${?})" "${?}" -# end_task "DONE (Path: ${MANIFEST_PATH}/${GITHUB_SHA}.tar.gz)" -# -# start_task "Setting Step Outputs" -# { -# printf "path=%s\n" "${MANIFEST_PATH}" -# printf "file=%s\n" "${MANIFEST_PATH}/${GITHUB_SHA}.tar.gz" -# printf "name=%s\n" "${GITHUB_SHA}.tar.gz" -# } >> "${GITHUB_OUTPUT}" -# end_task + start_task "Resolving the GITHUB_WORKSPACE path" + # Ensure GITHUB_WORKSPACE is provided or default to the repository root + if [[ -z "${GITHUB_WORKSPACE}" || ! -d "${GITHUB_WORKSPACE}" ]]; then + GITHUB_WORKSPACE="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)" + fi + end_task "DONE (Path: ${GITHUB_WORKSPACE})" + + start_task "Resolving the GITHUB_OUTPUT path" + # Ensure GITHUB_OUTPUT is provided or default to the repository root + if [[ -z "${GITHUB_OUTPUT}" ]]; then + GITHUB_OUTPUT="${TEMP_DIR}/workflow-output.txt" + fi + end_task "DONE (Path: ${GITHUB_OUTPUT})" + + start_task "Resolving the GITHUB_SHA hash" + if [[ -z "${GITHUB_SHA}" ]]; then + GITHUB_SHA="$(git rev-parse HEAD | tr -d '[:space:]')" || fail "ERROR (Exit Code: ${?})" "${?}" + fi + end_task "DONE (Commit: ${GITHUB_SHA})" + + start_task "Resolving the MANIFEST_PATH variable" + if [[ -z "${MANIFEST_PATH}" ]]; then + MANIFEST_PATH="${GITHUB_WORKSPACE}/.manifests/gradle" + fi + end_task "DONE (Path: ${MANIFEST_PATH})" + + start_task "Ensuring the MANIFEST_PATH location is present" + if [[ ! -d "${MANIFEST_PATH}" ]]; then + mkdir -p "${MANIFEST_PATH}" || fail "ERROR (Exit Code: ${?})" "${?}" + fi + end_task + + start_task "Resolving the SKOPEO_VERSION variable" + if [[ -z "${SKOPEO_VERSION}" ]]; then + SKOPEO_VERSION="v1.14.0" + fi + end_task "DONE (Version: ${SKOPEO_VERSION})" + + start_task "Resolving the SKOPEO_IMAGE_NAME variable" + if [[ -z "${SKOPEO_IMAGE_NAME}" ]]; then + SKOPEO_IMAGE_NAME="quay.io/skopeo/stable:${SKOPEO_VERSION}" + fi + end_task "DONE (Image Name: ${SKOPEO_IMAGE_NAME})" + + start_task "Checking for the DOCKER command" + if command -v docker >/dev/null 2>&1; then + DOCKER="$(command -v docker)" || fail "ERROR (Exit Code: ${?})" "${?}" + export DOCKER + else + fail "ERROR (Exit Code: ${?})" "${?}" + fi + end_task "DONE (Found: ${DOCKER})" + + start_task "Resolving the Docker Client Configuration" + SKOPEO_BIND_MOUNT="" + SKOPEO_CREDENTIAL_OPTS="" + DOCKER_CONFIG_DIR="${HOME}/.docker" + if [[ -d "${DOCKER_CONFIG_DIR}" ]]; then + SKOPEO_BIND_MOUNT="--volume ${DOCKER_CONFIG_DIR}:/tmp/docker" + SKOPEO_CREDENTIAL_OPTS="--authfile /tmp/docker/config.json" + fi + export SKOPEO_BIND_MOUNT SKOPEO_CREDENTIAL_OPTS + end_task "DONE" + + start_task "Checking for the SKOPEO command" + if command -v skopeo >/dev/null 2>&1; then + SKOPEO="$(command -v skopeo)" || fail "ERROR (Exit Code: ${?})" "${?}" + export SKOPEO + else + ${DOCKER} pull "${SKOPEO_IMAGE_NAME}" >/dev/null 2>&1 || fail "ERROR (Exit Code: ${?})" "${?}" + SKOPEO="${DOCKER} run ${SKOPEO_BIND_MOUNT} --rm --network host ${SKOPEO_IMAGE_NAME}" + export SKOPEO + fi + end_task "DONE (Found: ${SKOPEO})" + + start_task "Checking for the JQ command" + if command -v jq >/dev/null 2>&1; then + JQ="$(command -v jq)" || fail "ERROR (Exit Code: ${?})" "${?}" + export JQ + else + fail "ERROR (Exit Code: ${?})" "${?}" + fi + end_task "DONE (Found: ${JQ})" +end_group + +start_group "Prepare the Docker Image Information" + export DOCKER_REGISTRY DOCKER_TAG + + start_task "Resolving the DOCKER_REGISTRY variable" + if [[ -z "${DOCKER_REGISTRY}" ]]; then + DOCKER_REGISTRY="grpc.io" + fi + end_task "DONE (Registry: ${DOCKER_REGISTRY})" + + start_task "Resolving the DOCKER_TAG variable" + if [[ -z "${DOCKER_TAG}" ]]; then + DOCKER_TAG="$(echo "${GITHUB_SHA}" | tr -d '[:space:]' | cut -c1-8)" + fi + end_task "DONE (Tag: ${DOCKER_TAG})" + + + start_task "Resolving the Fully Qualified Image Name" + FQ_IMAGE_NAME="${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_TAG}" + end_task "DONE (Image: ${FQ_IMAGE_NAME})" +end_group + +start_group "Generate Docker Image Manifest (linux/amd64)" + ${SKOPEO} --override-os linux --override-arch amd64 inspect ${SKOPEO_CREDENTIAL_OPTS} --tls-verify=false "docker://${FQ_IMAGE_NAME}" | tee "${TEMP_DIR}/linux-amd64.manifest.json" || fail "SKOPEO ERROR (Exit Code: ${?})" "${?}" + ${JQ} -r '.Layers[]' "${TEMP_DIR}/linux-amd64.manifest.json" | tee "${TEMP_DIR}/linux-amd64.layers.json" >/dev/null 2>&1 || fail "JQ LAYER ERROR (Exit Code: ${?})" "${?}" + ${JQ} -r 'del(.RepoTags) | del(.LayersData) | del(.Digest) | del(.Name)' "${TEMP_DIR}/linux-amd64.manifest.json" | tee "${TEMP_DIR}/linux-amd64.comparable.json" >/dev/null 2>&1 || fail "JQ COMP ERROR (Exit Code: ${?})" "${?}" +end_group + +start_group "Generate Docker Image Manifest (linux/arm64)" + ${SKOPEO} --override-os linux --override-arch arm64 inspect ${SKOPEO_CREDENTIAL_OPTS} --tls-verify=false "docker://${FQ_IMAGE_NAME}" | tee "${TEMP_DIR}/linux-arm64.manifest.json" || fail "SKOPEO ERROR (Exit Code: ${?})" "${?}" + ${JQ} -r '.Layers[]' "${TEMP_DIR}/linux-arm64.manifest.json" | tee "${TEMP_DIR}/linux-arm64.layers.json" >/dev/null 2>&1 || fail "JQ LAYER ERROR (Exit Code: ${?})" "${?}" + ${JQ} -r 'del(.RepoTags) | del(.LayersData) | del(.Digest) | del(.Name)' "${TEMP_DIR}/linux-arm64.manifest.json" | tee "${TEMP_DIR}/linux-arm64.comparable.json" >/dev/null 2>&1 || fail "JQ COMP ERROR (Exit Code: ${?})" "${?}" +end_group + +start_group "Generating Final Release Manifests" + + start_task "Generating the manifest archive" + MANIFEST_FILES=("linux-amd64.manifest.json" "linux-amd64.layers.json" "linux-amd64.comparable.json") + MANIFEST_FILES+=("linux-arm64.manifest.json" "linux-arm64.layers.json" "linux-arm64.comparable.json") + tar -czf "${TEMP_DIR}/manifest.tar.gz" -C "${TEMP_DIR}" "${MANIFEST_FILES[@]}" >/dev/null 2>&1 || fail "TAR ERROR (Exit Code: ${?})" "${?}" + end_task + + start_task "Copying the manifest files" + cp "${TEMP_DIR}/manifest.tar.gz" "${MANIFEST_PATH}/${GITHUB_SHA}.tar.gz" || fail "COPY ERROR (Exit Code: ${?})" "${?}" + cp "${TEMP_DIR}"/*.json "${MANIFEST_PATH}/" || fail "COPY ERROR (Exit Code: ${?})" "${?}" + end_task "DONE (Path: ${MANIFEST_PATH}/${GITHUB_SHA}.tar.gz)" + + start_task "Setting Step Outputs" + { + printf "path=%s\n" "${MANIFEST_PATH}" + printf "file=%s\n" "${MANIFEST_PATH}/${GITHUB_SHA}.tar.gz" + printf "name=%s\n" "${GITHUB_SHA}.tar.gz" + } >> "${GITHUB_OUTPUT}" + end_task end_group diff --git a/.github/workflows/zxc-verify-docker-build-determinism.yaml b/.github/workflows/zxc-verify-docker-build-determinism.yaml index 7131aa7da..5c08312aa 100644 --- a/.github/workflows/zxc-verify-docker-build-determinism.yaml +++ b/.github/workflows/zxc-verify-docker-build-determinism.yaml @@ -1,5 +1,9 @@ # SPDX-License-Identifier: Apache-2.0 name: "ZXC: Verify Docker Build Determinism" +# Here, the ZXC prefix: +# Z - Ensures sort order such that this script appears at the bottom of the UI +# X - Indicates it's not for direct user consumption +# C - Indicates this is a 'workflow_call' based reusable workflow on: workflow_call: inputs: @@ -17,15 +21,25 @@ on: description: "Java JDK Version:" type: string required: false - default: "21.0.4" + default: "21.0.5" -# secrets: -# gradle-cache-username: -# description: "The username used to authenticate with the Gradle Build Cache Node." -# required: true -# gradle-cache-password: -# description: "The password used to authenticate with the Gradle Build Cache Node." -# required: true + workflow_dispatch: + inputs: + ref: + description: "The branch, tag, or commit to checkout:" + type: string + required: false + default: "" + java-distribution: + description: "Java JDK Distribution:" + type: string + required: false + default: "temurin" + java-version: + description: "Java JDK Version:" + type: string + required: false + default: "21.0.5" defaults: run: @@ -34,28 +48,28 @@ defaults: permissions: id-token: write contents: read + packages: write env: -# GRADLE_CACHE_USERNAME: ${{ secrets.gradle-cache-username }} -# GRADLE_CACHE_PASSWORD: ${{ secrets.gradle-cache-password }} DOCKER_MANIFEST_GENERATOR: .github/workflows/support/scripts/generate-docker-artifact-baseline.sh DOCKER_MANIFEST_PATH: ${{ github.workspace }}/.manifests/docker DOCKER_REGISTRY: localhost:5000 - DOCKER_IMAGE_NAME: consensus-node - DOCKER_CONTEXT_PATH: hedera-node/infrastructure/docker/containers/production-next/consensus-node + DOCKER_IMAGE_NAME: hashgraph/hedera-block-node + DOCKER_CONTEXT_PATH: server/docker SKOPEO_VERSION: v1.14.0 + OWNER: hashgraph jobs: generate-baseline: name: Generate Baseline runs-on: block-node-linux-medium -# outputs: -# sha: ${{ steps.commit.outputs.sha }} -# sha-abbrev: ${{ steps.commit.outputs.sha-abbrev }} -# source-date: ${{ steps.commit.outputs.source-date }} -# path: ${{ steps.baseline.outputs.path }} -# file: ${{ steps.baseline.outputs.file }} -# name: ${{ steps.baseline.outputs.name }} + outputs: + sha: ${{ steps.commit.outputs.sha }} + sha-abbrev: ${{ steps.commit.outputs.sha-abbrev }} + source-date: ${{ steps.commit.outputs.source-date }} + path: ${{ steps.baseline.outputs.path }} + file: ${{ steps.baseline.outputs.file }} + name: ${{ steps.baseline.outputs.name }} steps: - name: Harden Runner @@ -63,5 +77,308 @@ jobs: with: egress-policy: audit - - name: Hello World - run: echo "Testing Docker Build Determinism" + - name: Standardize Git Line Endings + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ inputs.ref }} + + - name: Authenticate to Google Cloud + id: google-auth + uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7 + with: + workload_identity_provider: "projects/235822363393/locations/global/workloadIdentityPools/hedera-builds-pool/providers/hedera-builds-gh-actions" + service_account: "swirlds-automation@hedera-registry.iam.gserviceaccount.com" + + - name: Setup Google Cloud SDK + uses: google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a # v2.1.2 + + - name: Retrieve Commit Hash + id: commit + run: | + echo "sha=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}" + echo "sha-abbrev=$(git rev-parse HEAD | tr -d '[:space:]' | cut -c1-8)" >> "${GITHUB_OUTPUT}" + echo "source-date=$(git log -1 --pretty=%ct)" >> "${GITHUB_OUTPUT}" + + - name: Baseline Existence Check + id: baseline + run: | + BASELINE_NAME="${{ steps.commit.outputs.sha }}.tar.gz" + BASELINE_PATH="gs://hedera-ci-ephemeral-artifacts/${{ github.repository }}/docker/baselines" + BASELINE_FILE="${BASELINE_PATH}/${BASELINE_NAME}" + BASELINE_EXISTS="false" + + if gsutil ls "${BASELINE_FILE}" >/dev/null 2>&1; then + BASELINE_EXISTS="false" + fi + + echo "exists=${BASELINE_EXISTS}" >> "${GITHUB_OUTPUT}" + echo "path=${BASELINE_PATH}" >> "${GITHUB_OUTPUT}" + echo "name=${BASELINE_NAME}" >> "${GITHUB_OUTPUT}" + echo "file=${BASELINE_FILE}" >> "${GITHUB_OUTPUT}" + + - name: Setup Java + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + with: + distribution: ${{ inputs.java-distribution }} + java-version: ${{ inputs.java-version }} + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + with: + cache-disabled: true + + - name: Install Skopeo and JQ + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: | + sudo apt-get update + sudo apt-get install --yes --no-install-recommends skopeo jq + + - name: Setup QEmu Support + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + + - name: Setup Docker Buildx Support + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + with: + version: v0.16.2 + driver-opts: network=host + buildkitd-config-inline: | + [registry."docker.io"] + mirrors = ["https://hub.mirror.docker.lat.ope.eng.hashgraph.io"] + + - name: Setup Local Docker Registry + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: docker run -d -p 5000:5000 --restart=always --name registry registry:latest + + - name: Show Docker Version + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: docker version + + - name: Show Docker Info + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: docker info + + - name: Build Gradle Artifacts + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: ./gradlew assemble --scan + + - name: Extract version + id: extract_version + run: | + VERSION=$(cat version.txt) + echo "VERSION=${VERSION}" >> $GITHUB_ENV + + - name: Build Docker Image + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 + env: + SOURCE_DATE_EPOCH: ${{ steps.commit.outputs.source-date }} + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + with: + push: true + no-cache: true + platforms: linux/amd64,linux/arm64 + build-args: | + SOURCE_DATE_EPOCH=${{ steps.commit.outputs.source-date }} + VERSION=${{ env.VERSION }} + context: ./${{ env.DOCKER_CONTEXT_PATH }} + file: ./${{ env.DOCKER_CONTEXT_PATH }}/Dockerfile + tags: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.commit.outputs.sha-abbrev }} + build-contexts: | + distributions=./server/build/distributions + + - name: Generate Manifest + id: manifest + env: + MANIFEST_PATH: ${{ env.DOCKER_MANIFEST_PATH }} + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: GITHUB_SHA="${{ steps.commit.outputs.sha }}" ${{ env.DOCKER_MANIFEST_GENERATOR }} + + - name: Amend Manifest with Gradle Artifacts + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + working-directory: ${{ env.DOCKER_MANIFEST_PATH }} + run: | + EXTRACTED_FILE_NAME="${{ steps.commit.outputs.sha }}.tar" + gunzip "${{ steps.manifest.outputs.name }}" + tar -rvf "${EXTRACTED_FILE_NAME}" -C "${{ github.workspace }}/${{ env.DOCKER_CONTEXT_PATH }}" ../../server/build/ + gzip "${EXTRACTED_FILE_NAME}" + + - name: Upload Baseline + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: gsutil cp "${{ steps.manifest.outputs.file }}" "${{ steps.baseline.outputs.file }}" + + verify-artifacts: + name: "Verify Artifacts (${{ join(matrix.os, ', ') }})" + runs-on: ${{ matrix.os }} + needs: + - generate-baseline + strategy: + fail-fast: false + matrix: + os: + - block-node-linux-medium + steps: + - name: Harden Runner + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 + with: + egress-policy: audit + + - name: Standardize Git Line Endings + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ inputs.ref }} + + - name: Setup Python + uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + with: + python-version: 3.9 + + - name: Install JQ (Linux) + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt-get update + sudo apt-get install --yes --no-install-recommends jq + + - name: Install Skopeo (Linux) + if: ${{ runner.os == 'Linux' }} + run: | + source /etc/os-release + if [[ "${VERSION_ID}" != "20.04" ]]; then + sudo apt-get install --yes --no-install-recommends skopeo + fi + + - name: Authenticate to Google Cloud + id: google-auth + uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7 + with: + workload_identity_provider: "projects/235822363393/locations/global/workloadIdentityPools/hedera-builds-pool/providers/hedera-builds-gh-actions" + service_account: "swirlds-automation@hedera-registry.iam.gserviceaccount.com" + + - name: Setup Google Cloud SDK + uses: google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a # v2.1.2 + env: + CLOUDSDK_PYTHON: ${{ format('{0}{1}', env.pythonLocation, runner.os == 'Windows' && '\python.exe' || '/bin/python3') }} + + - name: Download Baseline + env: + CLOUDSDK_PYTHON: ${{ format('{0}{1}', env.pythonLocation, runner.os == 'Windows' && '\python.exe' || '/bin/python3') }} + run: | + echo "Creating build directory: ./server/build/distributions" + mkdir -p ./server/build/distributions + + mkdir -p "${{ env.DOCKER_MANIFEST_PATH }}" + cd "${{ env.DOCKER_MANIFEST_PATH }}" + gsutil cp "${{ needs.generate-baseline.outputs.file }}" . + tar -xzf "${{ needs.generate-baseline.outputs.name }}" + cd ./server/build/distributions + cp *.tar "${{ github.workspace }}/server/build/distributions/" + echo "Copied Block Node server tar file to ${{ github.workspace }}/server/build/distributions/" + + - name: Determine Home Directory + id: home + run: echo "directory=$(tr -d '[:space:]' < <(cd ~ && pwd))" >> "${GITHUB_OUTPUT}" + + - name: Setup QEmu Support + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + + - name: Setup Docker Buildx Support + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 + with: + version: v0.16.2 + driver-opts: network=host + buildkitd-config-inline: | + [registry."docker.io"] + mirrors = ["https://hub.mirror.docker.lat.ope.eng.hashgraph.io"] + + - name: Setup Local Docker Registry + run: docker run -d -p 5000:5000 --restart=always --name registry registry:latest + + - name: Show Docker Version + run: docker version + + - name: Show Docker Info + run: docker info + + - name: Extract version + id: extract_version + run: | + VERSION=$(cat version.txt) + echo "VERSION=${VERSION}" >> $GITHUB_ENV + + - name: Build Docker Image + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 + env: + SOURCE_DATE_EPOCH: ${{ needs.generate-baseline.outputs.source-date }} + with: + push: true + no-cache: true + platforms: linux/amd64,linux/arm64 + build-args: | + SOURCE_DATE_EPOCH=${{ needs.generate-baseline.outputs.source-date }} + VERSION=${{ env.VERSION }} + context: ./${{ env.DOCKER_CONTEXT_PATH }} + file: ./${{ env.DOCKER_CONTEXT_PATH }}/Dockerfile + tags: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ needs.generate-baseline.outputs.sha-abbrev }} + build-contexts: | + distributions=./server/build/distributions + + - name: Regenerate Manifest + id: regen-manifest + env: + MANIFEST_PATH: ${{ env.DOCKER_MANIFEST_PATH }}/regenerated + run: GITHUB_SHA="${{ needs.generate-baseline.outputs.sha }}" ${{ env.DOCKER_MANIFEST_GENERATOR }} + + - name: Validate Layers (linux/amd64) + run: | + if ! diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-amd64.layers.json" "${{ steps.regen-manifest.outputs.path }}/linux-amd64.layers.json" >/dev/null 2>&1; then + echo "::group::Layer Differences" + diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-amd64.layers.json" "${{ steps.regen-manifest.outputs.path }}/linux-amd64.layers.json" + echo "::endgroup::" + exit 1 + fi + + - name: Validate Layers (linux/arm64) + run: | + if ! diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-arm64.layers.json" "${{ steps.regen-manifest.outputs.path }}/linux-arm64.layers.json" >/dev/null 2>&1; then + echo "::group::Layer Differences" + diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-arm64.layers.json" "${{ steps.regen-manifest.outputs.path }}/linux-arm64.layers.json" + echo "::endgroup::" + exit 1 + fi + + - name: Validate Full Manifest (linux/amd64) + run: | + if ! diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-amd64.comparable.json" "${{ steps.regen-manifest.outputs.path }}/linux-amd64.comparable.json" >/dev/null 2>&1; then + echo "::group::Layer Differences" + diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-amd64.comparable.json" "${{ steps.regen-manifest.outputs.path }}/linux-amd64.comparable.json" + echo "::endgroup::" + exit 1 + fi + + - name: Validate Full Manifest (linux/arm64) + run: | + if ! diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-arm64.comparable.json" "${{ steps.regen-manifest.outputs.path }}/linux-arm64.comparable.json" >/dev/null 2>&1; then + echo "::group::Layer Differences" + diff -u "${{ env.DOCKER_MANIFEST_PATH }}/linux-arm64.comparable.json" "${{ steps.regen-manifest.outputs.path }}/linux-arm64.comparable.json" + echo "::endgroup::" + exit 1 + fi + + - name: Publish Manifests + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + if: ${{ steps.regen-manifest.conclusion == 'success' && failure() && !cancelled() }} + with: + name: Docker Manifests [${{ join(matrix.os, ', ') }}] + path: ${{ env.DOCKER_MANIFEST_PATH }}/** diff --git a/.github/workflows/zxc-verify-gradle-build-determinism.yaml b/.github/workflows/zxc-verify-gradle-build-determinism.yaml index 7e224fac4..b3c05502a 100644 --- a/.github/workflows/zxc-verify-gradle-build-determinism.yaml +++ b/.github/workflows/zxc-verify-gradle-build-determinism.yaml @@ -21,22 +21,33 @@ on: description: "Java JDK Version:" type: string required: false - default: "21.0.4" + default: "21.0.5" -# workflow_dispatch: -# inputs: -# version: -# description: 'Release tag:' -# type: string -# required: false + workflow_dispatch: + inputs: + ref: + description: "The branch, tag, or commit to checkout:" + type: string + required: false + default: "" + java-distribution: + description: "Java JDK Distribution:" + type: string + required: false + default: "temurin" + java-version: + description: "Java JDK Version:" + type: string + required: false + default: "21.0.5" defaults: run: shell: bash permissions: + id-token: write contents: read - packages: write env: GRADLE_MANIFEST_PATH: ${{ github.workspace }}/.manifests/gradle @@ -52,6 +63,7 @@ jobs: path: ${{ steps.baseline.outputs.path }} file: ${{ steps.baseline.outputs.file }} name: ${{ steps.baseline.outputs.name }} + steps: - name: Harden Runner uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 @@ -74,6 +86,16 @@ jobs: with: cache-disabled: true + - name: Authenticate to Google Cloud + id: google-auth + uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7 + with: + workload_identity_provider: "projects/235822363393/locations/global/workloadIdentityPools/hedera-builds-pool/providers/hedera-builds-gh-actions" + service_account: "swirlds-automation@hedera-registry.iam.gserviceaccount.com" + + - name: Setup Google Cloud SDK + uses: google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a # v2.1.2 + - name: Retrieve Commit Hash id: commit run: echo "sha=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}" @@ -97,14 +119,20 @@ jobs: - name: Build Artifacts id: gradle-build + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} run: ./gradlew assemble --scan - name: Generate Manifest id: manifest env: MANIFEST_PATH: ${{ env.GRADLE_MANIFEST_PATH }} + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} run: ${{ env.GRADLE_MANIFEST_GENERATOR }} + - name: Upload Baseline + if: ${{ steps.baseline.outputs.exists == 'false' && !failure() && !cancelled() }} + run: gsutil cp "${{ steps.manifest.outputs.file }}" "${{ steps.baseline.outputs.file }}" + verify-artifacts: name: "Verify Artifacts (${{ join(matrix.os, ', ') }})" runs-on: ${{ matrix.os }} @@ -116,10 +144,7 @@ jobs: os: - ubuntu-22.04 - ubuntu-20.04 - - windows-2022 - - windows-2019 - block-node-linux-medium - - block-node-linux-large steps: - name: Harden Runner uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 @@ -152,30 +177,26 @@ jobs: with: cache-disabled: true -# - name: Setup CoreUtils (macOS) -# if: ${{ runner.os == 'macOS' }} -# run: brew install coreutils - -# - name: Authenticate to Google Cloud -# id: google-auth -# uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7 -# with: -# workload_identity_provider: "projects/235822363393/locations/global/workloadIdentityPools/hedera-builds-pool/providers/hedera-builds-gh-actions" -# service_account: "swirlds-automation@hedera-registry.iam.gserviceaccount.com" - -# - name: Setup Google Cloud SDK -# uses: google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a # v2.1.2 -# env: -# CLOUDSDK_PYTHON: ${{ format('{0}{1}', env.pythonLocation, runner.os == 'Windows' && '\python.exe' || '/bin/python3') }} -# + - name: Authenticate to Google Cloud + id: google-auth + uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7 + with: + workload_identity_provider: "projects/235822363393/locations/global/workloadIdentityPools/hedera-builds-pool/providers/hedera-builds-gh-actions" + service_account: "swirlds-automation@hedera-registry.iam.gserviceaccount.com" + + - name: Setup Google Cloud SDK + uses: google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a # v2.1.2 + env: + CLOUDSDK_PYTHON: ${{ format('{0}{1}', env.pythonLocation, runner.os == 'Windows' && '\python.exe' || '/bin/python3') }} + - name: Download Baseline env: CLOUDSDK_PYTHON: ${{ format('{0}{1}', env.pythonLocation, runner.os == 'Windows' && '\python.exe' || '/bin/python3') }} run: | mkdir -p "${GRADLE_MANIFEST_PATH}" cd "${GRADLE_MANIFEST_PATH}" -# gsutil cp "${{ needs.generate-baseline.outputs.file }}" . -# tar -xzf "${{ needs.generate-baseline.outputs.name }}" + gsutil cp "${{ needs.generate-baseline.outputs.file }}" . + tar -xzf "${{ needs.generate-baseline.outputs.name }}" - name: Build Artifacts id: gradle-build @@ -191,18 +212,18 @@ jobs: working-directory: ${{ github.workspace }}/server/build/libs run: sha256sum -c "${GRADLE_MANIFEST_PATH}/applications.sha256" -# - name: Compare Application Manifests -# run: | -# if ! diff -u "${GRADLE_MANIFEST_PATH}/applications.sha256" "${{ steps.regen-manifest.outputs.applications }}" >/dev/null 2>&1; then -# echo "::group::Application Manifest Differences" -# diff -u "${GRADLE_MANIFEST_PATH}/applications.sha256" "${{ steps.regen-manifest.outputs.applications }}" -# echo "::endgroup::" -# exit 1 -# fi -# -# - name: Publish Manifests -# uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 -# if: ${{ steps.regen-manifest.conclusion == 'success' && failure() && !cancelled() }} -# with: -# name: Gradle Manifests [${{ join(matrix.os, ', ') }}] -# path: ${{ env.GRADLE_MANIFEST_PATH }}/** + - name: Compare Application Manifests + run: | + if ! diff -u "${GRADLE_MANIFEST_PATH}/applications.sha256" "${{ steps.regen-manifest.outputs.applications }}" >/dev/null 2>&1; then + echo "::group::Application Manifest Differences" + diff -u "${GRADLE_MANIFEST_PATH}/applications.sha256" "${{ steps.regen-manifest.outputs.applications }}" + echo "::endgroup::" + exit 1 + fi + + - name: Publish Manifests + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + if: ${{ steps.regen-manifest.conclusion == 'success' && failure() && !cancelled() }} + with: + name: Gradle Manifests [${{ join(matrix.os, ', ') }}] + path: ${{ env.GRADLE_MANIFEST_PATH }}/** diff --git a/gradle/toolchain-versions.properties b/gradle/toolchain-versions.properties index 04e458fae..6a3c7e0e7 100644 --- a/gradle/toolchain-versions.properties +++ b/gradle/toolchain-versions.properties @@ -1 +1 @@ -jdk=21.0.4 +jdk=21.0.5 diff --git a/server/docker/Dockerfile b/server/docker/Dockerfile index 02a229a2a..7adb26ace 100644 --- a/server/docker/Dockerfile +++ b/server/docker/Dockerfile @@ -3,22 +3,32 @@ # Define Global Build Arguments # ######################################################################################################################## -ARG UBUNTU_TAG="focal-20230605" +ARG UBUNTU_TAG="mantic-20240216" +ARG SOURCE_DATE_EPOCH="0" ######################################################################################################################## # # Setup Builder Image # ######################################################################################################################## -FROM ubuntu:${UBUNTU_TAG} AS openjdk-builder +FROM ubuntu:${UBUNTU_TAG} AS java-builder-interim +# Define Build Arguments +ARG SOURCE_DATE_EPOCH # Define Standard Environment Variables -ENV LC_ALL=C.UTF-8 ENV DEBIAN_FRONTEND=noninteractive +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 # Install basic OS utilities for building -RUN apt-get update && \ - apt-get install --yes tar gzip gnupg2 curl +RUN --mount=type=bind,source=./repro-sources-list.sh,target=/usr/local/bin/repro-sources-list.sh \ + repro-sources-list.sh && \ + apt-get update && \ + apt-get install --yes --no-install-recommends tar gzip curl ca-certificates && \ + apt-get autoclean --yes && \ + apt-get clean all --yes && \ + rm -rf /var/log/ && \ + rm -rf /var/cache/ ########################## #### Java Setup #### @@ -28,21 +38,17 @@ RUN set -eux; \ ARCH="$(dpkg --print-architecture)"; \ case "${ARCH}" in \ aarch64|arm64) \ - ESUM='e184dc29a6712c1f78754ab36fb48866583665fa345324f1a79e569c064f95e9'; \ - BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.1%2B12/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.1_12.tar.gz'; \ - ;; \ - amd64|i386:x86-64) \ - ESUM='1a6fa8abda4c5caed915cfbeeb176e7fbd12eb6b222f26e290ee45808b529aa1'; \ - BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.1%2B12/OpenJDK21U-jdk_x64_linux_hotspot_21.0.1_12.tar.gz'; \ - ;; \ - ppc64el|powerpc:common64) \ - ESUM='9574828ef3d735a25404ced82e09bf20e1614f7d6403956002de9cfbfcb8638f'; \ - BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.1%2B12/OpenJDK21U-jdk_ppc64le_linux_hotspot_21.0.1_12.tar.gz'; \ - ;; \ + ESUM='6482639ed9fd22aa2e704cc366848b1b3e1586d2bf1213869c43e80bca58fe5c'; \ + BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.5%2B11/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.5_11.tar.gz' \ + ;; \ + amd64|i386:x86-64) \ + ESUM='3c654d98404c073b8a7e66bffb27f4ae3e7ede47d13284c132d40a83144bfd8c'; \ + BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.5%2B11/OpenJDK21U-jdk_x64_linux_hotspot_21.0.5_11.tar.gz' \ + ;; \ *) \ - echo "Unsupported arch: ${ARCH}"; \ - exit 1; \ - ;; \ + echo "Unsupported arch: ${ARCH}"; \ + exit 1; \ + ;; \ esac; \ curl -LfsSo /tmp/openjdk.tar.gz ${BINARY_URL}; \ echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \ @@ -55,48 +61,13 @@ RUN set -eux; \ ; \ rm -f /tmp/openjdk.tar.gz /usr/local/java/lib/src.zip; -######################################################################################################################## -# -# Build Final Image -# -######################################################################################################################## -FROM ubuntu:${UBUNTU_TAG} AS openjdk-base - -# Define Standard Environment Variables -ENV LC_ALL=C.UTF-8 -ENV DEBIAN_FRONTEND=noninteractive -ENV JAVA_VERSION="jdk-21.0.1+12" -ENV JAVA_HOME="/usr/local/java/" - -# Fetch Validated Java Binaries -COPY --from=openjdk-builder /usr/local/java/ /usr/local/java/ - -# Install Basic OS Requirements -RUN apt-get update && \ - apt-get install --yes --no-install-recommends tar gzip openssl curl && \ - apt-get autoremove --yes && \ - apt-get autoclean --yes && \ - apt-get clean all --yes && \ - rm -rf /var/lib/{apt,dpkg,cache,log}/ - -# Expose the port that the application will run on -EXPOSE 8080 +RUN groupadd --gid 2000 hedera && \ + useradd --no-user-group --create-home --uid 2000 --gid 2000 --shell /bin/bash hedera # Define version ARG VERSION -# Create a non-root user and group -ARG UNAME=hedera -ARG UID=2000 -ARG GID=2000 - -# Configure the standard user account -RUN groupadd --gid ${GID} ${UNAME} && \ - useradd --no-user-group --create-home --uid ${UID} --gid ${GID} --shell /bin/bash ${UNAME} - -USER $UNAME - # Set the working directory inside the container WORKDIR /app @@ -112,6 +83,57 @@ RUN mkdir -p /app/logs/config # Copy the logging properties file COPY logging.properties /app/logs/config/logging.properties +WORKDIR / + +# Ensure proper file permissions +RUN chown -R 2000:2000 /app + +######################################## +#### Deterministic Build Hack #### +######################################## + +# === Workarounds below will not be needed when https://github.com/moby/buildkit/pull/4057 is merged === +# NOTE: PR #4057 has been merged but will not be available until the v0.13.x series of releases. +# Limit the timestamp upper bound to SOURCE_DATE_EPOCH. +# Workaround for https://github.com/moby/buildkit/issues/3180 +RUN find $( ls / | grep -E -v "^(dev|mnt|proc|sys)$" ) \ + -newermt "@${SOURCE_DATE_EPOCH}" -writable -xdev \ + | xargs touch --date="@${SOURCE_DATE_EPOCH}" --no-dereference + +########################## + +FROM scratch AS java-builder +COPY --from=java-builder-interim / / + + +######################################################################################################################## +# +# Build Final Image +# +######################################################################################################################## +FROM java-builder AS production-image + +# Define Build Arguments +ARG SOURCE_DATE_EPOCH + +# Define Standard Environment Variables +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 +ENV DEBIAN_FRONTEND=noninteractive + +ENV JAVA_VERSION="jdk-21.0.5+11" +ENV JAVA_HOME=/usr/local/java +ENV PATH=${JAVA_HOME}/bin:${PATH} + +# Install Java +COPY --from=java-builder ${JAVA_HOME}/ ${JAVA_HOME}/ + +# Expose the port that the application will run on +EXPOSE 8080/tcp + +USER hedera +WORKDIR /app + # HEALTHCHECK for liveness and readiness HEALTHCHECK --interval=30s --timeout=10s --start-period=3s --retries=3 \ CMD curl -f http://localhost:8080/healthz/livez || exit 1 && \ diff --git a/server/docker/docker-build.sh b/server/docker/docker-build.sh index c31c5bad4..d28680ea5 100755 --- a/server/docker/docker-build.sh +++ b/server/docker/docker-build.sh @@ -10,8 +10,12 @@ VERSION=$1 echo "Building image [block-node-server:${VERSION}]" echo +SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) # run docker build -docker buildx build --load -t "block-node-server:${VERSION}" --build-context distributions=../distributions --build-arg VERSION="${VERSION}" . || exit "${?}" +docker buildx build --load -t "block-node-server:${VERSION}" \ + --build-context distributions=../distributions \ + --build-arg VERSION="${VERSION}" \ + --build-arg SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH}" . || exit "${?}" echo echo "Image [block-node-server:${VERSION}] built successfully!" diff --git a/server/docker/repro-sources-list.sh b/server/docker/repro-sources-list.sh new file mode 100755 index 000000000..1bd0b01ff --- /dev/null +++ b/server/docker/repro-sources-list.sh @@ -0,0 +1,97 @@ +#!/bin/bash +# +# Copyright The repro-sources-list.sh 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. + +# ----------------------------------------------------------------------------- +# repro-sources-list.sh: +# configures /etc/apt/sources.list and similar files for installing packages from a snapshot. +# +# This script is expected to be executed inside Dockerfile. +# +# The following distributions are supported: +# - debian:11 (/etc/apt/sources.list) +# - debian:12 (/etc/apt/sources.list.d/debian.sources) +# - ubuntu:22.04 (/etc/apt/sources.list) +# - ubuntu:23.10 (/etc/apt/sources.list) +# - archlinux (/etc/pacman.d/mirrorlist) +# +# For the further information, see https://github.com/reproducible-containers/repro-sources-list.sh +# ----------------------------------------------------------------------------- + +set -eux -o pipefail + +. /etc/os-release + +keep_apt_cache() { + rm -f /etc/apt/apt.conf.d/docker-clean + echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache +} + +case "${ID}" in +"debian") + : "${SNAPSHOT_ARCHIVE_BASE:=http://snapshot-cloudflare.debian.org/archive/}" + : "${BACKPORTS:=}" + case "${VERSION_ID}" in + "10" | "11") + : "${SOURCE_DATE_EPOCH:=$(stat --format=%Y /etc/apt/sources.list)}" + ;; + *) + : "${SOURCE_DATE_EPOCH:=$(stat --format=%Y /etc/apt/sources.list.d/debian.sources)}" + rm -f /etc/apt/sources.list.d/debian.sources + ;; + esac + snapshot="$(printf "%(%Y%m%dT%H%M%SZ)T\n" "${SOURCE_DATE_EPOCH}")" + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}debian/${snapshot} ${VERSION_CODENAME} main" >/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}debian-security/${snapshot} ${VERSION_CODENAME}-security main" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}debian/${snapshot} ${VERSION_CODENAME}-updates main" >>/etc/apt/sources.list + if [ "${BACKPORTS}" = 1 ]; then echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}debian/${snapshot} ${VERSION_CODENAME}-backports main" >>/etc/apt/sources.list; fi + keep_apt_cache + ;; +"ubuntu") + : "${SNAPSHOT_ARCHIVE_BASE:=http://snapshot.ubuntu.com/}" + : "${SOURCE_DATE_EPOCH:=$(stat --format=%Y /etc/apt/sources.list)}" + snapshot="$(printf "%(%Y%m%dT%H%M%SZ)T\n" "${SOURCE_DATE_EPOCH}")" + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME} main restricted" >/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME}-updates main restricted" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME} universe" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME}-updates universe" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME} multiverse" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME}-updates multiverse" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME}-security main restricted" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME}-security universe" >>/etc/apt/sources.list + echo "deb [check-valid-until=no] ${SNAPSHOT_ARCHIVE_BASE}ubuntu/${snapshot} ${VERSION_CODENAME}-security multiverse" >>/etc/apt/sources.list + keep_apt_cache + # http://snapshot.ubuntu.com is redirected to https, so we have to install ca-certificates + export DEBIAN_FRONTEND=noninteractive + apt-get -o Acquire::https::Verify-Peer=false update >&2 + apt-get -o Acquire::https::Verify-Peer=false install -y ca-certificates >&2 + ;; +"arch") + : "${SNAPSHOT_ARCHIVE_BASE:=http://archive.archlinux.org/}" + : "${SOURCE_DATE_EPOCH:=$(stat --format=%Y /var/log/pacman.log)}" + export SOURCE_DATE_EPOCH + # shellcheck disable=SC2016 + date -d "@${SOURCE_DATE_EPOCH}" "+Server = ${SNAPSHOT_ARCHIVE_BASE}repos/%Y/%m/%d/\$repo/os/\$arch" >/etc/pacman.d/mirrorlist + ;; +*) + echo >&2 "Unsupported distribution: ${ID}" + exit 1 + ;; +esac + +: "${WRITE_SOURCE_DATE_EPOCH:=/dev/null}" +echo "${SOURCE_DATE_EPOCH}" >"${WRITE_SOURCE_DATE_EPOCH}" +echo "SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}"