Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ci): add support for docker image determinism checking #10662

Merged
merged 3 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/flow-artifact-determinism.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ permissions:
contents: read

jobs:
check:
check-gradle:
name: Gradle
uses: ./.github/workflows/zxc-verify-gradle-build-determinism.yaml
with:
Expand All @@ -59,3 +59,14 @@ jobs:
secrets:
gradle-cache-username: ${{ secrets.GRADLE_CACHE_USERNAME }}
gradle-cache-password: ${{ secrets.GRADLE_CACHE_PASSWORD }}

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.1' }}
secrets:
gradle-cache-username: ${{ secrets.GRADLE_CACHE_USERNAME }}
gradle-cache-password: ${{ secrets.GRADLE_CACHE_PASSWORD }}
19 changes: 17 additions & 2 deletions .github/workflows/node-flow-pull-request-checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ jobs:
gradle-cache-password: ${{ secrets.GRADLE_CACHE_PASSWORD }}
snyk-token: ${{ secrets.SNYK_TOKEN }}

artifact-determinism:
name: Artifact Determinism
gradle-determinism:
name: Gradle Determinism
uses: ./.github/workflows/zxc-verify-gradle-build-determinism.yaml
needs:
- dependency-check
Expand All @@ -287,3 +287,18 @@ jobs:
secrets:
gradle-cache-username: ${{ secrets.GRADLE_CACHE_USERNAME }}
gradle-cache-password: ${{ secrets.GRADLE_CACHE_PASSWORD }}

docker-determinism:
name: Docker Determinism
uses: ./.github/workflows/zxc-verify-docker-build-determinism.yaml
needs:
- dependency-check
- spotless
if: ${{ github.actor != 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository }}
with:
ref: ${{ github.event.inputs.ref || '' }}
java-distribution: temurin
java-version: 21.0.1
secrets:
gradle-cache-username: ${{ secrets.GRADLE_CACHE_USERNAME }}
gradle-cache-password: ${{ secrets.GRADLE_CACHE_PASSWORD }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env bash
set -o pipefail
set +e

readonly RELEASE_LIB_PATH="hedera-node/data/lib"
readonly RELEASE_APPS_PATH="hedera-node/data/apps"
readonly DOCKER_IMAGE_NAME="main-network-node"

GROUP_ACTIVE="false"

function fail {
printf '%s\n' "$1" >&2 ## Send message to stderr. Exclude >&2 if you don't want it that way.
if [[ "${GROUP_ACTIVE}" == "true" ]]; then
end_group
fi
exit "${2-1}" ## Return a code specified by $2 or 1 by default.
}

function start_group {
if [[ "${GROUP_ACTIVE}" == "true" ]]; then
end_group
fi

GROUP_ACTIVE="true"
printf "::group::%s\n" "${1}"
}

function end_group {
GROUP_ACTIVE="false"
printf "::endgroup::\n"
}

function log {
local message="${1}"
shift
# shellcheck disable=SC2059
printf "${message}" "${@}"
}

function log_line {
local message="${1}"
shift
# shellcheck disable=SC2059
printf "${message}\n" "${@}"
}

function start_task {
local message="${1}"
shift
# shellcheck disable=SC2059
printf "${message} .....\t" "${@}"
}

function end_task {
printf "%s\n" "${1:-DONE}"
}

start_group "Configuring Environment"
# Access workflow environment variables
export GITHUB_WORKSPACE GITHUB_SHA GITHUB_OUTPUT MANIFEST_PATH DOCKER_REGISTRY DOCKER_TAG

start_task "Initializing Temporary Directory"
TEMP_DIR="$(mktemp -d)" || fail "ERROR (Exit Code: ${?})" "${?}"
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 "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
fail "ERROR (Exit Code: ${?})" "${?}"
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})"

start_task "Checking for prebuilt libraries"
ls -al "${GITHUB_WORKSPACE}/${RELEASE_LIB_PATH}"/*.jar >/dev/null 2>&1 || fail "ERROR (Exit Code: ${?})" "${?}"
end_task "FOUND (Path: ${GITHUB_WORKSPACE}/${RELEASE_LIB_PATH}/*.jar)"

start_task "Checking for prebuilt applications"
ls -al "${GITHUB_WORKSPACE}/${RELEASE_APPS_PATH}"/*.jar >/dev/null 2>&1 || fail "ERROR (Exit Code: ${?})" "${?}"
end_task "FOUND (Path: ${GITHUB_WORKSPACE}/${RELEASE_APPS_PATH}/*.jar)"
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 --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)' "${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 --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)' "${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"
tar -czf "${TEMP_DIR}/manifest.tar.gz" -C "${TEMP_DIR}" linux-amd64.manifest.json linux-amd64.layers.json linux-amd64.comparable.json linux-arm64.manifest.json linux-arm64.layers.json linux-arm64.comparable.json >/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