Skip to content

Build Linux

Build Linux #31

Workflow file for this run

name: Build Linux
on:
workflow_call:
inputs:
binary_list:
required: true
type: string
workflow_dispatch:
inputs:
binary_list:
description: 'Binaries bundled into the plain tar.gz artifacts (packaged debs/rpms always ship the full set)'
required: false
type: string
default: 'api-blockchain-scanner-daemon,api-web-server,dns-server,node-daemon,wallet-address-generator,wallet-cli,wallet-rpc-daemon'
concurrency:
group: release_linux-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-22.04
timeout-minutes: 180
permissions:
contents: read
strategy:
fail-fast: false
matrix:
arch: [aarch64, x86_64]
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
fetch-depth: 0 # git describe needs history for the dispatch fallback version
persist-credentials: false # the checkout is only built, never pushed
# Register qemu binfmt handlers so `docker run --platform linux/arm64`
# works on the arm64 matrix leg (needed for the debian:12 deb builder).
- uses: docker/setup-qemu-action@v3
- name: Extract version from tag
id: get_version
run: |
VERSION=${GITHUB_REF#refs/tags/}
VERSION=${VERSION#v}
if [ "$VERSION" = "$GITHUB_REF" ]; then
VERSION="$(git describe --tags --abbrev=0 2>/dev/null | sed -e 's/^v//' || true)"
fi
if [ -z "$VERSION" ]; then
VERSION="0.0.0-ci"
fi
# Validate the grammar up front, so a non-semver tag fails here with a clear message
# instead of deep inside a packaging script (the same grammar the builders enforce).
if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "derived version '$VERSION' is not X.Y.Z[-suffix]; tag a semver release" >&2
exit 1
fi
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "Version extracted: $VERSION"
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: ${{ matrix.arch }}-unknown-linux-gnu
- name: Install cross-compilation tools
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
- name: Install build dependencies
run: |
if [ "${{ matrix.arch }}" = "aarch64" ]; then
sudo dpkg --add-architecture arm64
sudo sed -i -E 's/^deb /deb [arch=amd64] /' /etc/apt/sources.list
{
echo 'deb [arch=arm64 signed-by=/usr/share/keyrings/ubuntu-archive-keyring.gpg] http://ports.ubuntu.com/ubuntu-ports jammy main restricted universe multiverse'
echo 'deb [arch=arm64 signed-by=/usr/share/keyrings/ubuntu-archive-keyring.gpg] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted universe multiverse'
echo 'deb [arch=arm64 signed-by=/usr/share/keyrings/ubuntu-archive-keyring.gpg] http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse'
echo 'deb [arch=arm64 signed-by=/usr/share/keyrings/ubuntu-archive-keyring.gpg] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted universe multiverse'
} | sudo tee /etc/apt/sources.list.d/ubuntu-ports-arm64.list > /dev/null
sudo apt-get update
sudo apt-get install -y \
gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
libdbus-1-dev:arm64 \
libusb-1.0-0-dev:arm64 \
libudev-dev:arm64
else
sudo apt-get update
sudo apt-get install -y \
libdbus-1-dev \
libusb-1.0-0-dev \
libudev-dev
fi
sudo apt-get install -y zip pkg-config
- name: Build
env:
PKG_CONFIG_ALLOW_CROSS_aarch64_unknown_linux_gnu: "1"
PKG_CONFIG_LIBDIR_aarch64_unknown_linux_gnu: "/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig"
run: |
cargo build --release --locked --target ${{ matrix.arch }}-unknown-linux-gnu --features trezor,ledger
# Shared hicolor icon set for the GUI packages (deb and rpm builders
# consume it; avoids ImageMagick in the fedora container).
- name: Pre-generate icons
run: |
docker run --rm --platform linux/${{ matrix.arch }} -v "$PWD":/work -w /work debian:12 \
bash -ec "
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq && apt-get install -y -qq imagemagick >/dev/null
packaging/make-icons.sh build-tools/assets/node-gui-icon_512.png dist/assets/icons
"
# GH-hosted ubuntu runners ship qemu-user-static + binfmt, so
# `docker run --platform linux/arm64` works out of the box: the arm64 leg
# runs the deb builder inside an arm64 debian:12 container. The same
# scripts run locally via packaging/test-local.sh.
- name: Create Debian packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
ARCH=${{ matrix.arch }}
if [ "$ARCH" = "x86_64" ]; then DEBARCH=amd64; else DEBARCH=arm64; fi
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/deb/build.sh \
--package node --version "$VERSION" \
--debarch $DEBARCH \
--binaries-dir /work/target/$ARCH-unknown-linux-gnu/release \
--out /work/dist
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/deb/build.sh \
--package gui --version "$VERSION" \
--debarch $DEBARCH \
--gui-binary /work/target/$ARCH-unknown-linux-gnu/release/node-gui \
--repo-root /work --out /work/dist
# Arch-matched container (qemu on the arm64 leg): native strip works and
# the binaries run under emulation so help2man generates real man pages.
#
# Container image pins live in packaging/images.env, shared with the
# other steps that source it and with packaging/test-local.sh.
- name: Create RPM packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
source packaging/images.env
ARCH=${{ matrix.arch }}
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/rpm/build.sh \
--package node --rpmarch $ARCH \
--version "$VERSION" \
--binaries-dir /work/target/$ARCH-unknown-linux-gnu/release \
--out /work/dist
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/rpm/build.sh \
--package gui --rpmarch $ARCH \
--version "$VERSION" \
--gui-binary /work/target/$ARCH-unknown-linux-gnu/release/node-gui \
--repo-root /work --out /work/dist
# Install smoke tests in fresh containers (same images as production use)
- name: Smoke test Debian packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
ARCH=${{ matrix.arch }}
if [ "$ARCH" = "x86_64" ]; then DEBARCH=amd64; else DEBARCH=arm64; fi
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/checks/smoke-deb.sh dist/Mintlayer_Node_linux_${VERSION}_${DEBARCH}.deb mintlayer-node node
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/checks/smoke-deb.sh dist/Mintlayer_Node_GUI_linux_${VERSION}_${DEBARCH}.deb mintlayer-node-gui gui
- name: Smoke test RPM packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
source packaging/images.env
ARCH=${{ matrix.arch }}
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/checks/smoke-rpm.sh dist/Mintlayer_Node_linux_${VERSION}_${ARCH}.rpm mintlayer-node node
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/checks/smoke-rpm.sh dist/Mintlayer_Node_GUI_linux_${VERSION}_${ARCH}.rpm mintlayer-node-gui gui
# Arch images are amd64-only: the x86_64 leg is arch-matched (native
# strip, ldd-based dependencies, help2man man pages), the arm64 leg
# repackages cross-target with stubs — same as the local rpm legs.
- name: Create Arch packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
source packaging/images.env
ARCH=${{ matrix.arch }}
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/arch/build.sh \
--package node --arch "$ARCH" \
--version "$VERSION" \
--binaries-dir /work/target/$ARCH-unknown-linux-gnu/release \
--out /work/dist
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/arch/build.sh \
--package gui --arch "$ARCH" \
--version "$VERSION" \
--gui-binary /work/target/$ARCH-unknown-linux-gnu/release/node-gui \
--repo-root /work --out /work/dist
# x86_64 leg: arch-matched container (native strip, ldd-based
# dependencies, help2man man pages), so the package installs natively.
# aarch64 leg: Docker Hub publishes no arm64 Arch image, so
# smoke-arch.sh assembles an Arch Linux ARM chroot and installs/runs the
# package there through the qemu binfmt handlers registered above — the
# same emulation the deb/rpm legs use for their arm64 smoke tests.
- name: Smoke test Arch packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
source packaging/images.env
ARCH=${{ matrix.arch }}
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/checks/smoke-arch.sh dist/Mintlayer_Node_linux_${VERSION}_${ARCH}.pkg.tar.zst mintlayer-node node "$ARCH"
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/checks/smoke-arch.sh dist/Mintlayer_Node_GUI_linux_${VERSION}_${ARCH}.pkg.tar.zst mintlayer-node-gui gui "$ARCH"
- name: Package Mintlayer Node (without GUI) as tar.gz
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
BINARY_LIST: ${{ inputs.binary_list }}
run: |
mkdir -p Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}
IFS=',' read -ra BINARIES <<< "$BINARY_LIST"
for binary in "${BINARIES[@]}"; do
cp target/${{ matrix.arch }}-unknown-linux-gnu/release/$binary Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}/mintlayer-$binary
done
tar -czvf Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}.tar.gz Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}
- name: Package Mintlayer Node GUI as tar.gz
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
mkdir -p Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}
cp target/${{ matrix.arch }}-unknown-linux-gnu/release/node-gui Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}/mintlayer-node-gui
tar -czvf Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}.tar.gz Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}
- name: Upload Node DEB Artifact (without GUI)
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_deb
path: dist/Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch == 'x86_64' && 'amd64' || 'arm64' }}.deb
- name: Upload GUI DEB Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_deb
path: dist/Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch == 'x86_64' && 'amd64' || 'arm64' }}.deb
- name: Upload Node RPM Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_rpm
path: dist/Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.rpm
- name: Upload GUI RPM Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_rpm
path: dist/Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.rpm
- name: Upload Node PKG Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_pkg
path: dist/Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.pkg.tar.zst
- name: Upload GUI PKG Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_pkg
path: dist/Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.pkg.tar.zst
- name: List tar.gz files
run: |
echo "Matching tar.gz files:"
ls -l Mintlayer_Node_linux_*.tar.gz
- name: Upload Node GUI tar.gz Artifact
uses: actions/upload-artifact@v4
continue-on-error: true
with:
name: Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_tar.gz
path: Mintlayer_Node_GUI_linux_*.tar.gz
- name: Upload Node tar.gz Artifact (without GUI)
uses: actions/upload-artifact@v4
continue-on-error: true
with:
name: Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_tar.gz
path: Mintlayer_Node_linux_*.tar.gz