From 66b251a87fd3d4d3e2c381e62d66dc5ae19554c5 Mon Sep 17 00:00:00 2001 From: "C.Lee Taylor" <47312074+leet4tari@users.noreply.github.com> Date: Fri, 6 Dec 2024 08:02:28 +0200 Subject: [PATCH] =?UTF-8?q?chore(ci):=20bring=20cross-compiling=20in=20lin?= =?UTF-8?q?e=20with=20main=20repo=20and=20improve=20d=E2=80=A6=20(#211)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Description Bring cross-compiling in line with main repo Build x64 and arm64 docker images Improve docker tagging Motivation and Context Improve multi-arch support How Has This Been Tested? Builds in local fork --- .dockerignore | 13 ++ .github/workflows/build_docker.yml | 221 ++++++++++++++++++ .github/workflows/docker.yml | 46 ---- .github/workflows/pr_signed_commits_check.yml | 24 ++ .license.ignore | 1 + Cross.toml | 14 ++ Dockerfile.cross-compile | 94 ++++++++ scripts/cross_compile_ubuntu_18-pre-build.sh | 73 ++++-- 8 files changed, 422 insertions(+), 64 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/build_docker.yml delete mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/pr_signed_commits_check.yml create mode 100644 Dockerfile.cross-compile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..25ff8232 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.dockerignore +Dockerfile* + +node_modules +npm-debug.log +.npmrc + +.git +.gitignore +.github + +target + diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml new file mode 100644 index 00000000..7991c12c --- /dev/null +++ b/.github/workflows/build_docker.yml @@ -0,0 +1,221 @@ +--- +name: Build sha-p2pool docker images + +'on': + push: + paths-ignore: + - '**/*.md' + tags: + - 'v[0-9]+.[0-9]+.[0-9]*' + branches: + - 'build-all-*' + - 'build-dockers-*' + schedule: + - cron: '05 00 * * *' + workflow_dispatch: + inputs: + version: + type: string + description: 'override image tag/version' + tag_alias: + type: string + description: 'image tag alias' + +env: + DOCKER_IMAGE: sha-p2pool + DAYS_to_EXPIRE: 30 + +concurrency: + # https://docs.github.com/en/actions/examples/using-concurrency-expressions-and-a-test-matrix + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/v') || github.ref != 'refs/heads/development' }} + +permissions: {} + +jobs: + builds_envs_setup: + name: Build envs setup + runs-on: ubuntu-latest + outputs: + version: ${{ steps.envs_setup.outputs.version }} + + steps: + - name: Prep docker build environment + id: envs_setup + shell: bash + run: | + #TESHA_SHORT=$(git rev-parse --short HEAD) + TESHA_SHORT=${GITHUB_SHA::7} + if [[ "${{ github.ref }}" =~ ^refs\/tags\/v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then + VERSION="${{ github.ref_name }}_$(date -u '+%Y%m%d')_${TESHA_SHORT}" + echo "TAG_ALIAS=${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}" >> $GITHUB_ENV + else + if [ -z "${{ inputs.version }}" ] ; then + VERSION="${{ github.ref_name }}_$(date -u '+%Y%m%d')_${TESHA_SHORT}" + else + VERSION=${{ inputs.version }} + fi + fi + echo "Setting ${VERSION} as docker tag" + echo "VERSION=${VERSION}" >> $GITHUB_ENV + echo "version=${VERSION}" >> $GITHUB_OUTPUT + if [ ! -z "${{ inputs.tag_alias }}" ] ; then + echo "Setup tag_alias" + echo "TAG_ALIAS=${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }}:${{ inputs.tag_alias }}" >> $GITHUB_ENV + fi + + docker_build: + name: Docker building + needs: builds_envs_setup + runs-on: ubuntu-latest + + outputs: + version: ${{ steps.envs_setup.outputs.version }} + + permissions: + contents: read + packages: write + + strategy: + fail-fast: false + matrix: + platform: [amd64, arm64] + + steps: + - name: Checkout p2pool explorer + uses: actions/checkout@v4 + + - name: Set up QEMU for Docker + uses: docker/setup-qemu-action@v3 + with: + platforms: all + + - name: Expire setup + shell: bash + run: | + if [[ "${{ github.ref }}" =~ ^refs\/tags\/v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then + echo "No Expire for release" + else + echo "EXPIRATION=${{ env.DAYS_to_EXPIRE }}d" >> $GITHUB_ENV + fi + echo "VERSION=${{ needs.builds_envs_setup.outputs.version }}" >> $GITHUB_ENV + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }} + ${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }} + tags: | + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha + labels: | + maintainer=${{ github.actor }} + quay.expires-after=${{ env.EXPIRATION }} + org.opencontainers.image.vendor=TariLabs + org.opencontainers.image.title=${{ env.DOCKER_IMAGE }} + org.opencontainers.image.description=Multi-arch Docker image for ${{ env.DOCKER_IMAGE }} + org.opencontainers.image.url=https://github.com/${{ github.repository }} + org.opencontainers.image.source=https://github.com/${{ github.repository }} + flavor: | + suffix=-${{ matrix.platform }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Login to Docker Image Provider + uses: docker/login-action@v3 + with: + registry: ${{ secrets.DOCKER_PROVIDER }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Docker image build and push + id: docker_build + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.cross-compile + platforms: linux/${{ matrix.platform }} + push: true + provenance: false + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ env.VERSION }} + tags: | + ${{ steps.meta.outputs.tags }} + ${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }}:${{ env.VERSION }}-${{ matrix.platform }} + ghcr.io/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}:latest-${{ matrix.platform }} + ${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }}:latest-${{ matrix.platform }} + ${{ env.TAG_ALIAS }} + outputs: | + type=registry,annotation-manifest-descriptor.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation-manifest-descriptor.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }},annotation.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }},annotation-index.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation-index.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }} + + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} + + create-manifest: + needs: [ builds_envs_setup, docker_build ] + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Log in to Registries + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + echo "${{ secrets.DOCKER_PASSWORD }}" | docker login ${{ secrets.DOCKER_PROVIDER }} -u ${{ secrets.DOCKER_USERNAME }} --password-stdin + + # Create and push the multi-arch image to both registries + - name: Push multi-arch image + run: | + DVERSION=${{ needs.builds_envs_setup.outputs.version }} + for DREGISTRY in ghcr.io quay.io; do + if [[ "${DREGISTRY}" == "ghcr.io" ]]; then + repo="${{ github.repository_owner }}" + else + repo="${{ secrets.DOCKER_REPO }}" + fi + + echo "Registry run - ${DREGISTRY}" + echo "Creating multi-arch image for ${repo}/${{ env.DOCKER_IMAGE }} for ${DVERSION}" + docker manifest create \ + ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:${DVERSION} \ + --amend ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest-amd64 \ + --amend ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest-arm64 + + echo "Inspect multi-arch image for ${repo}/${{ env.DOCKER_IMAGE }}" + docker manifest inspect ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:${DVERSION} > manifest.json + cat manifest.json + echo "Pushing multi-arch image for ${repo}/${{ env.DOCKER_IMAGE }}" + docker manifest push ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:${DVERSION} || true + + if [[ "${{ github.ref }}" =~ ^refs\/tags\/v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then + echo "Release Multi-Arch Tag" + docker manifest create \ + ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:${{ github.ref_name }} \ + --amend ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest-amd64 \ + --amend ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest-arm64 + docker manifest push ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:${{ github.ref_name }} || true + fi + + echo "Latest Multi-Arch tag" + docker manifest create \ + ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest \ + --amend ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest-amd64 \ + --amend ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest-arm64 + #docker manifest annotate ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest --file manifest.json + docker manifest push ${DREGISTRY}/${repo}/${{ env.DOCKER_IMAGE }}:latest || true + + done diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 7592ee28..00000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Build docker images - -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]*" - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Login to Quay.io - uses: docker/login-action@v3 - with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: quay.io/tarilabs/sha-p2pool - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Setup docker context for buildx - id: buildx-context - run: docker context create builders - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - endpoint: builders - - - name: Build and push - uses: docker/build-push-action@v6 - with: - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64 #, linux/arm64 diff --git a/.github/workflows/pr_signed_commits_check.yml b/.github/workflows/pr_signed_commits_check.yml new file mode 100644 index 00000000..cd72593e --- /dev/null +++ b/.github/workflows/pr_signed_commits_check.yml @@ -0,0 +1,24 @@ +--- +# Checks if the comments are signed or not +name: PR - Signed commits check + +'on': + pull_request_target + +concurrency: + # https://docs.github.com/en/actions/examples/using-concurrency-expressions-and-a-test-matrix + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/v') || github.ref != 'refs/heads/development' || github.ref != 'refs/heads/nextnet' || github.ref != 'refs/heads/stagenet' }} + +permissions: {} + +jobs: + check-signed-commits: + name: Check signed commits in PR + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Check signed commits in PR + uses: 1Password/check-signed-commits-action@v1 diff --git a/.license.ignore b/.license.ignore index e69de29b..0a483e23 100644 --- a/.license.ignore +++ b/.license.ignore @@ -0,0 +1 @@ +./Dockerfile.cross-compile diff --git a/Cross.toml b/Cross.toml index 3c38e5cc..d24a0e1a 100644 --- a/Cross.toml +++ b/Cross.toml @@ -36,3 +36,17 @@ passthrough = [ [target.x86_64-unknown-linux-gnu] image = "ubuntu:18.04" pre-build = "./scripts/cross_compile_ubuntu_18-pre-build.sh" + +[target.x86_64-unknown-linux-gnu.env] +passthrough = [ + "PKG_CONFIG_ALLOW_CROSS=true", +] + +[target.riscv64gc-unknown-linux-gnu] +image = "ubuntu:22.04" +pre-build = "./scripts/cross_compile_ubuntu_18-pre-build.sh" + +[target.riscv64gc-unknown-linux-gnu.env] +passthrough = [ + "PKG_CONFIG_ALLOW_CROSS=true", +] diff --git a/Dockerfile.cross-compile b/Dockerfile.cross-compile new file mode 100644 index 00000000..8ad8273b --- /dev/null +++ b/Dockerfile.cross-compile @@ -0,0 +1,94 @@ +# syntax = docker/dockerfile:1.3 + +# https://hub.docker.com/_/rust +ARG RUST_VERSION=1.81 +ARG OS_BASE=bookworm + +# rust source compile with cross platform build support +FROM --platform=$BUILDPLATFORM rust:$RUST_VERSION-${OS_BASE} as builder + +# Declare to make available +ARG BUILDPLATFORM +ARG BUILDARCH +ARG TARGETPLATFORM +ARG TARGETARCH +ARG RUST_TOOLCHAIN +ARG RUST_TARGET +ARG RUST_VERSION +ARG OS_BASE + +ENV CARGO_HTTP_MULTIPLEXING=false + +ARG VERSION=0.0.1 +ARG TARI_NETWORK +ARG TARI_TARGET_NETWORK + +# Build profile, release by default +ARG BUILD_PROFILE=release +ENV BUILD_PROFILE=${BUILD_PROFILE} + +# Extra Cargo flags +ARG RUSTFLAGS="" +ENV RUSTFLAGS="${RUSTFLAGS}" + +# Extra Cargo features +ARG FEATURES="" +ENV FEATURES=${FEATURES} + +# Disable Prompt During Packages Installation +ARG DEBIAN_FRONTEND=noninteractive + +WORKDIR /tari +COPY . . + +RUN apt-get update && \ + sh /tari/scripts/install_ubuntu_dependencies.sh + +RUN if [ "${BUILDARCH}" != "${TARGETARCH}" ] ; then \ + # Run script to help setup cross-compile environment + . /tari/scripts/cross_compile_tooling.sh ; \ + fi + +RUN if [ -n "${RUST_TOOLCHAIN}" ] ; then \ + # Install a non-standard toolchain if it has been requested. + # By default we use the toolchain specified in rust-toolchain.toml + rustup toolchain install ${RUST_TOOLCHAIN} --force-non-host ; \ + fi + +# Build application +RUN cargo build --profile ${BUILD_PROFILE} \ + --features "${FEATURES}" --locked --bin sha_p2pool + +# ARG is not resolved in COPY so we have to hack around it by copying the +# binary to a temporary location +RUN cp -v /tari/target/${BUILD_TARGET}${BUILD_PROFILE}/sha_p2pool /tari/sha_p2pool + +# Create runtime base minimal image for the target platform executables +FROM --platform=$TARGETPLATFORM bitnami/minideb:${OS_BASE} as runtime + +# Disable Prompt During Packages Installation +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y tini curl && \ + # Docker image reduction + apt-get clean all && \ + apt-get autoremove --assume-yes && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN groupadd --gid 1000 tari && \ + useradd --create-home --no-log-init --shell /bin/bash \ + --home-dir /var/tari \ + --uid 1000 --gid 1000 tari + +WORKDIR /tari + +# Copy tari over from the build stage +COPY --from=builder /tari/sha_p2pool /usr/local/bin + +# Copy licenses +COPY LICENSE-* ./ + +USER tari + +ENTRYPOINT ["tini", "--", "/usr/local/bin/sha_p2pool"] diff --git a/scripts/cross_compile_ubuntu_18-pre-build.sh b/scripts/cross_compile_ubuntu_18-pre-build.sh index 203b1575..3f6624b7 100755 --- a/scripts/cross_compile_ubuntu_18-pre-build.sh +++ b/scripts/cross_compile_ubuntu_18-pre-build.sh @@ -5,40 +5,69 @@ set -e -USAGE="Usage: $0 target build ie: x86_64-unknown-linux-gnu or aarch64-unknown-linux-gnu" +# APT Proxy for quicker testing +#export HTTP_PROXY_APT=http://apt-proxy.local:3142 +if [ ! -z "${HTTP_PROXY_APT}" ] && [ -d "/etc/apt/apt.conf.d/" ]; then + echo "Setup apt proxy - ${HTTP_PROXY_APT}" + cat << APT-EoF > /etc/apt/apt.conf.d/proxy.conf +Acquire { + HTTP::proxy "${HTTP_PROXY_APT}"; + #HTTPS::proxy "http://127.0.0.1:8080"; +} +APT-EoF +fi + +USAGE="Usage: $0 {target build} + where target build is one of the following: + x86_64-unknown-linux-gnu or + aarch64-unknown-linux-gnu or + riscv64gc-unknown-linux-gnu +" if [ "$#" == "0" ]; then - echo "$USAGE" + echo -e "${USAGE}" exit 1 fi if [ -z "${CROSS_DEB_ARCH}" ]; then - echo "Should be run from cross, which sets the env CROSS_DEB_ARCH" + echo -e "Should be run from cross, which sets the env 'CROSS_DEB_ARCH' + amd64 + arm64 + riscv64 +" exit 1 fi +DEBIAN_FRONTEND=${DEBIAN_FRONTEND:-noninteractive} +# Hack of Note! +TimeZone=${TimeZone:-"Etc/GMT"} +ln -snf /usr/share/zoneinfo/${TimeZone} /etc/localtime +echo ${TimeZone} > /etc/timezone + targetBuild="${1}" nativeRunTime=$(uname -m) echo "Native RunTime is ${nativeRunTime}" if [ "${nativeRunTime}" == "x86_64" ]; then nativeArch=amd64 - if [ "${targetBuild}" == "aarch64-unknown-linux-gnu" ]; then - targetArch=arm64 - targetPlatform=aarch64 - else - targetArch=amd64 - targetPlatform=x86-64 - fi elif [ "${nativeRunTime}" == "aarch64" ]; then nativeArch=arm64 - if [ "${targetBuild}" == "x86_64-unknown-linux-gnu" ]; then - targetArch=amd64 - targetPlatform=x86-64 - fi elif [ "${nativeRunTime}" == "riscv64" ]; then nativeArch=riscv64 - echo "ToDo!" +else + echo "!!Unsupport platform!!" + exit 1 +fi + +if [ "${targetBuild}" == "aarch64-unknown-linux-gnu" ]; then + targetArch=arm64 + targetPlatform=aarch64 +elif [ "${targetBuild}" == "x86_64-unknown-linux-gnu" ]; then + targetArch=amd64 + targetPlatform=x86-64 +elif [ "${targetBuild}" == "riscv64gc-unknown-linux-gnu" ]; then + targetArch=riscv64 + targetPlatform=riscv64 else echo "!!Unsupport platform!!" exit 1 @@ -63,6 +92,7 @@ apt-get install --no-install-recommends --assume-yes \ libsqlite3-0 \ libreadline-dev \ git \ + make \ cmake \ dh-autoreconf \ clang \ @@ -91,7 +121,7 @@ if [ "${CROSS_DEB_ARCH}" != "${nativeArch}" ]; then . /etc/lsb-release ubuntu_tag=${DISTRIB_CODENAME} - if [ "${crossArch}" == "arm64" ]; then + if [[ "${crossArch}" =~ ^(arm|riscv)64$ ]]; then cat << EoF > /etc/apt/sources.list.d/${ubuntu_tag}-${crossArch}.list deb [arch=${crossArch}] http://ports.ubuntu.com/ubuntu-ports ${ubuntu_tag} main restricted universe multiverse # deb-src [arch=${crossArch}] http://ports.ubuntu.com/ubuntu-ports ${ubuntu_tag} main restricted universe multiverse @@ -143,12 +173,14 @@ deb [arch=amd64] http://security.ubuntu.com/ubuntu/ ${ubuntu_tag}-security multi EoF fi + dpkg --print-architecture dpkg --add-architecture ${CROSS_DEB_ARCH} + dpkg --print-architecture apt-get update # scripts/install_ubuntu_dependencies-cross_compile.sh x86-64 +# pkg-config-${targetPlatform}-linux-gnu \ apt-get --assume-yes install \ - pkg-config-${targetPlatform}-linux-gnu \ gcc-${targetPlatform}-linux-gnu \ g++-${targetPlatform}-linux-gnu @@ -156,12 +188,17 @@ EoF apt-get --assume-yes install \ libsqlite3-dev:${CROSS_DEB_ARCH} \ libudev-dev:${CROSS_DEB_ARCH} \ - libhidapi-dev:${CROSS_DEB_ARCH} + libhidapi-dev:${CROSS_DEB_ARCH} \ + libssl-dev:${CROSS_DEB_ARCH} fi +rustup show + rustup target add ${targetBuild} rustup toolchain install stable-${targetBuild} --force-non-host rustup target list --installed rustup toolchain list + +rustup show