Aded mipsel support to YABB #36
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-License-Identifier: 0BSD | |
| # Copyright (c) 2023-2026 Aryan Ameri | |
| # | |
| # ============================================================================= | |
| # YABB Release Workflow | |
| # Cross-compiles static binaries for all architectures | |
| # - amd64, arm64, armv7l, ppc64le, loong64, mips64el: Zig cross-compilation | |
| # - riscv64: RISCstar toolchain (Zig's musl has ABI incompatibility) | |
| # - armv5l: Bootlin toolchain (Zig's musl emits NEON instructions) | |
| # Note: armv7l requires NEON (Zig's musl is compiled with NEON optimisations) | |
| # | |
| # Workflow structure: | |
| # 1. setup-nim: Build Nim once and upload as artifact (prevents download race) | |
| # 2. build: 8 parallel jobs download Nim artifact + their toolchain | |
| # 3. release: Collect all binaries and publish | |
| # | |
| # Archives use zstd compression (level 19) for best size | |
| # ============================================================================= | |
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| permissions: | |
| contents: write | |
| jobs: | |
| # ========================================================================== | |
| # Setup Nim - Build once, share with all build jobs | |
| # This prevents 8 jobs from racing to download from nim-lang.org | |
| # ========================================================================== | |
| setup-nim: | |
| name: Setup Nim | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Load versions from versions.env | |
| run: | | |
| if [ -f versions.env ]; then | |
| source versions.env | |
| echo "NIM_VERSION=$NIM_VERSION" >> $GITHUB_ENV | |
| echo "Loaded: NIM=$NIM_VERSION" | |
| else | |
| echo "ERROR: versions.env not found" | |
| exit 1 | |
| fi | |
| - name: Cache Nim | |
| uses: actions/cache@v4 | |
| id: nim-cache | |
| with: | |
| path: ~/nim-${{ env.NIM_VERSION }} | |
| key: nim-${{ env.NIM_VERSION }}-${{ runner.os }} | |
| - name: Install Nim from source | |
| if: steps.nim-cache.outputs.cache-hit != 'true' | |
| run: | | |
| cd ~ | |
| echo "Installing Nim ${{ env.NIM_VERSION }} from source..." | |
| curl -fsSL --proto '=https' --tlsv1.2 -LO \ | |
| "https://nim-lang.org/download/nim-${{ env.NIM_VERSION }}.tar.xz" | |
| tar xf "nim-${{ env.NIM_VERSION }}.tar.xz" | |
| cd "nim-${{ env.NIM_VERSION }}" | |
| sh build.sh | |
| ./bin/nim c -d:release koch | |
| ./koch boot -d:release | |
| ./koch nimble | |
| cd .. | |
| rm -f "nim-${{ env.NIM_VERSION }}.tar.xz" | |
| echo "Nim ${{ env.NIM_VERSION }} installed successfully" | |
| - name: Create Nim tarball for artifact | |
| run: | | |
| cd ~ | |
| tar -cf nim-${{ env.NIM_VERSION }}.tar nim-${{ env.NIM_VERSION }} | |
| ls -lh nim-${{ env.NIM_VERSION }}.tar | |
| - name: Upload Nim as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: nim-${{ env.NIM_VERSION }} | |
| path: ~/nim-${{ env.NIM_VERSION }}.tar | |
| retention-days: 1 | |
| compression-level: 0 | |
| # ========================================================================== | |
| # Build - Cross-compile for all architectures in parallel | |
| # ========================================================================== | |
| build: | |
| name: Build ${{ matrix.arch }} | |
| needs: setup-nim | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| target: x86_64-linux-musl | |
| nimcpu: "" | |
| march: "-march=x86_64 -mno-avx -mno-avx2" | |
| toolchain: zig | |
| - arch: arm64 | |
| target: aarch64-linux-musl | |
| nimcpu: "--cpu:arm64" | |
| march: "-march=armv8-a -mtune=cortex_a72" | |
| toolchain: zig | |
| - arch: armv7l | |
| target: arm-linux-musleabihf | |
| nimcpu: "--cpu:arm" | |
| march: "-mcpu=baseline" | |
| toolchain: zig | |
| - arch: riscv64 | |
| nimcpu: "--cpu:riscv64" | |
| toolchain: riscstar | |
| - arch: ppc64le | |
| target: powerpc64le-linux-musl | |
| nimcpu: "--cpu:powerpc64" | |
| march: "-mcpu=pwr8 -mtune=pwr9" | |
| toolchain: zig | |
| - arch: loong64 | |
| target: loongarch64-linux-musl | |
| nimcpu: "--cpu:loongarch64" | |
| march: "-march=loongarch64" | |
| toolchain: zig | |
| - arch: mips64el | |
| target: mips64el-linux-muslabi64 | |
| nimcpu: "--cpu:mips64" | |
| march: "-mcpu=mips64r2" | |
| toolchain: zig | |
| - arch: mipsel | |
| target: mipsel-linux-musleabihf | |
| nimcpu: "--cpu:mips" | |
| march: "-mcpu=mips32r2" | |
| toolchain: zig | |
| - arch: armv5l | |
| nimcpu: "--cpu:arm" | |
| toolchain: bootlin | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Load versions from versions.env | |
| run: | | |
| if [ -f versions.env ]; then | |
| source versions.env | |
| echo "NIM_VERSION=$NIM_VERSION" >> $GITHUB_ENV | |
| echo "ZIG_VERSION=$ZIG_VERSION" >> $GITHUB_ENV | |
| echo "RISCSTAR_VERSION=$RISCSTAR_VERSION" >> $GITHUB_ENV | |
| echo "BOOTLIN_ARMV5_VERSION=$BOOTLIN_ARMV5_VERSION" >> $GITHUB_ENV | |
| echo "Loaded: NIM=$NIM_VERSION, ZIG=$ZIG_VERSION, RISCSTAR=$RISCSTAR_VERSION, BOOTLIN=$BOOTLIN_ARMV5_VERSION" | |
| else | |
| echo "ERROR: versions.env not found" | |
| exit 1 | |
| fi | |
| - name: Download Nim artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: nim-${{ env.NIM_VERSION }} | |
| path: /home/runner | |
| - name: Extract Nim | |
| run: | | |
| cd ~ | |
| tar -xf nim-${{ env.NIM_VERSION }}.tar | |
| rm nim-${{ env.NIM_VERSION }}.tar | |
| echo "Nim ${{ env.NIM_VERSION }} ready" | |
| - name: Cache toolchain | |
| uses: actions/cache@v4 | |
| id: toolchain-cache | |
| with: | |
| path: | | |
| ${{ matrix.toolchain == 'zig' && '~/zig' || '' }} | |
| ${{ matrix.toolchain == 'riscstar' && '~/riscv64-toolchain' || '' }} | |
| ${{ matrix.toolchain == 'bootlin' && '~/armv5-toolchain' || '' }} | |
| key: toolchain-${{ matrix.toolchain }}-${{ matrix.toolchain == 'zig' && env.ZIG_VERSION || matrix.toolchain == 'riscstar' && env.RISCSTAR_VERSION || env.BOOTLIN_ARMV5_VERSION }} | |
| - name: Install Zig | |
| if: matrix.toolchain == 'zig' && steps.toolchain-cache.outputs.cache-hit != 'true' | |
| run: | | |
| cd ~ | |
| ZIG_FILE="zig-x86_64-linux-${{ env.ZIG_VERSION }}.tar.xz" | |
| echo "Installing Zig ${{ env.ZIG_VERSION }}..." | |
| # Get expected SHA256 from Zig's official JSON API | |
| echo "Fetching checksum from Zig JSON API..." | |
| EXPECTED_SHA=$(curl -fsSL --proto '=https' --tlsv1.2 \ | |
| "https://ziglang.org/download/index.json" | \ | |
| jq -r '."${{ env.ZIG_VERSION }}"."x86_64-linux".shasum') | |
| if [ -z "$EXPECTED_SHA" ] || [ "$EXPECTED_SHA" = "null" ]; then | |
| echo "ERROR: Could not fetch checksum for Zig ${{ env.ZIG_VERSION }}" | |
| exit 1 | |
| fi | |
| echo "Expected SHA256: $EXPECTED_SHA" | |
| # Download tarball | |
| curl -fsSL --proto '=https' --tlsv1.2 \ | |
| -o "${ZIG_FILE}" \ | |
| "https://ziglang.org/download/${{ env.ZIG_VERSION }}/${ZIG_FILE}" | |
| # Verify checksum | |
| echo "Verifying SHA256 checksum..." | |
| echo "$EXPECTED_SHA $ZIG_FILE" | sha256sum -c - | |
| # Extract | |
| tar xf "${ZIG_FILE}" | |
| rm -rf zig | |
| mv "zig-x86_64-linux-${{ env.ZIG_VERSION }}" zig | |
| rm -f "${ZIG_FILE}" | |
| echo "Zig ${{ env.ZIG_VERSION }} installed and verified successfully" | |
| - name: Install RISCstar RISC-V64 toolchain | |
| if: matrix.toolchain == 'riscstar' && steps.toolchain-cache.outputs.cache-hit != 'true' | |
| run: | | |
| cd ~ | |
| echo "Installing RISCstar RISC-V64 toolchain ${{ env.RISCSTAR_VERSION }}..." | |
| # NOTE: RISCstar does not publish checksums - using TLS for transport security only | |
| curl -fsSL --proto '=https' --tlsv1.2 \ | |
| -o "riscstar-toolchain.tar.xz" \ | |
| "https://releases.riscstar.com/toolchain/${{ env.RISCSTAR_VERSION }}/riscstar-toolchain-${{ env.RISCSTAR_VERSION }}+qemu-x86_64-riscv64-none-linux-musl.tar.xz" | |
| mkdir -p riscv64-toolchain | |
| tar xf "riscstar-toolchain.tar.xz" -C riscv64-toolchain --strip-components=1 | |
| rm -f "riscstar-toolchain.tar.xz" | |
| echo "RISCstar ${{ env.RISCSTAR_VERSION }} installed successfully" | |
| - name: Install Bootlin ARMv5 toolchain | |
| if: matrix.toolchain == 'bootlin' && steps.toolchain-cache.outputs.cache-hit != 'true' | |
| run: | | |
| cd ~ | |
| TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/armv5-eabi/tarballs" | |
| TOOLCHAIN_FILE="${{ env.BOOTLIN_ARMV5_VERSION }}.tar.xz" | |
| CHECKSUM_FILE="${{ env.BOOTLIN_ARMV5_VERSION }}.sha256" | |
| echo "Installing Bootlin ARMv5 toolchain ${{ env.BOOTLIN_ARMV5_VERSION }}..." | |
| # Secure download: HTTPS-only, TLS 1.2 minimum, fail on errors | |
| echo "Downloading toolchain and checksum..." | |
| curl -fsSL --proto '=https' --tlsv1.2 \ | |
| -o "${TOOLCHAIN_FILE}" \ | |
| "${TOOLCHAIN_URL}/${TOOLCHAIN_FILE}" | |
| curl -fsSL --proto '=https' --tlsv1.2 \ | |
| -o "${CHECKSUM_FILE}" \ | |
| "${TOOLCHAIN_URL}/${CHECKSUM_FILE}" | |
| # Verify SHA256 checksum | |
| echo "Verifying SHA256 checksum..." | |
| sha256sum -c "${CHECKSUM_FILE}" | |
| # Extract toolchain | |
| mkdir -p armv5-toolchain | |
| tar xf "${TOOLCHAIN_FILE}" -C armv5-toolchain --strip-components=1 | |
| rm -f "${TOOLCHAIN_FILE}" "${CHECKSUM_FILE}" | |
| echo "Bootlin ${{ env.BOOTLIN_ARMV5_VERSION }} installed and verified successfully" | |
| - name: Add tools to PATH | |
| run: | | |
| echo "$HOME/nim-${{ env.NIM_VERSION }}/bin" >> $GITHUB_PATH | |
| echo "$HOME/.nimble/bin" >> $GITHUB_PATH | |
| case "${{ matrix.toolchain }}" in | |
| zig) echo "$HOME/zig" >> $GITHUB_PATH ;; | |
| riscstar) echo "$HOME/riscv64-toolchain/bin" >> $GITHUB_PATH ;; | |
| bootlin) echo "$HOME/armv5-toolchain/bin" >> $GITHUB_PATH ;; | |
| esac | |
| - name: Install zigcc | |
| if: matrix.toolchain == 'zig' | |
| run: | | |
| if ! command -v zigcc &> /dev/null; then | |
| nimble install -y zigcc | |
| fi | |
| zigcc --version | |
| - name: Verify toolchain (Zig) | |
| if: matrix.toolchain == 'zig' | |
| run: | | |
| nim --version | |
| nimble --version | |
| zig version | |
| zigcc --version | |
| - name: Verify toolchain (RISCstar) | |
| if: matrix.toolchain == 'riscstar' | |
| run: | | |
| nim --version | |
| nimble --version | |
| riscv64-none-linux-musl-gcc --version | |
| - name: Verify toolchain (Bootlin) | |
| if: matrix.toolchain == 'bootlin' | |
| run: | | |
| nim --version | |
| nimble --version | |
| arm-buildroot-linux-musleabi-gcc --version | |
| - name: Install project dependencies | |
| run: | | |
| # Pre-emptively remove any stale nim package from cache | |
| rm -rf ~/.nimble/pkgs2/nim-* | |
| nimble install -d --accept | |
| nimble setup | |
| # Remove again in case nimble reinstalled nim from registry | |
| rm -rf ~/.nimble/pkgs2/nim-* | |
| - name: Build ${{ matrix.arch }} binary (Zig) | |
| if: matrix.toolchain == 'zig' | |
| run: | | |
| echo "Cross-compiling for ${{ matrix.arch }} using Zig..." | |
| nim c -d:release --opt:speed --mm:orc -d:lto \ | |
| --cc:clang --clang.exe:zigcc --clang.linkerexe:zigcc \ | |
| ${{ matrix.nimcpu }} \ | |
| --passC:'-target ${{ matrix.target }}' \ | |
| --passL:'-target ${{ matrix.target }}' \ | |
| --passC:'${{ matrix.march }}' \ | |
| --passC:-ffunction-sections --passC:-fdata-sections \ | |
| --passC:-flto=thin --passL:-flto=thin \ | |
| --passL:-Wl,--gc-sections --passL:-s \ | |
| -o:bin/yabb src/yabb.nim | |
| echo "Built: bin/yabb (${{ matrix.arch }})" | |
| - name: Build ${{ matrix.arch }} binary (RISCstar) | |
| if: matrix.toolchain == 'riscstar' | |
| run: | | |
| echo "Cross-compiling for ${{ matrix.arch }} using RISCstar..." | |
| # Create symlinks for Nim cross-compilation (expects riscv64-linux-gnu-*) | |
| cd ~/riscv64-toolchain/bin | |
| for tool in gcc g++ ar ld strip objcopy objdump ranlib nm readelf; do | |
| ln -sf "riscv64-none-linux-musl-$tool" "riscv64-linux-gnu-$tool" | |
| done | |
| cd /home/runner/work/yabb/yabb | |
| # Note: Using -flto=auto for parallel LTO (GCC's default is single-threaded) | |
| nim c -d:release --opt:speed --mm:orc \ | |
| ${{ matrix.nimcpu }} \ | |
| --gcc.exe:riscv64-none-linux-musl-gcc \ | |
| --gcc.linkerexe:riscv64-none-linux-musl-gcc \ | |
| --passL:-static \ | |
| --passC:-O3 --passC:-march=rv64gc \ | |
| --passC:-ffunction-sections --passC:-fdata-sections \ | |
| --passC:-flto=auto --passL:-flto=auto \ | |
| --passL:-Wl,--gc-sections \ | |
| -o:bin/yabb src/yabb.nim | |
| riscv64-none-linux-musl-strip -s bin/yabb | |
| echo "Built: bin/yabb (${{ matrix.arch }})" | |
| - name: Build ${{ matrix.arch }} binary (Bootlin) | |
| if: matrix.toolchain == 'bootlin' | |
| run: | | |
| echo "Cross-compiling for ${{ matrix.arch }} using Bootlin..." | |
| # Create symlinks for Nim cross-compilation (--cpu:arm expects arm-linux-gnueabihf-*) | |
| # Bootlin uses wrapper scripts that look for .br_real, so symlink both | |
| cd ~/armv5-toolchain/bin | |
| for tool in gcc g++ ar ld strip objcopy objdump ranlib nm readelf; do | |
| ln -sf "arm-buildroot-linux-musleabi-$tool" "arm-linux-gnueabihf-$tool" | |
| # Also symlink the .br_real files that Bootlin wrappers need | |
| if [ -f "arm-buildroot-linux-musleabi-$tool.br_real" ]; then | |
| ln -sf "arm-buildroot-linux-musleabi-$tool.br_real" "arm-linux-gnueabihf-$tool.br_real" | |
| fi | |
| done | |
| cd /home/runner/work/yabb/yabb | |
| # Note: Using -flto=auto for parallel LTO (GCC's default is single-threaded) | |
| nim c -d:release --opt:speed --mm:orc \ | |
| ${{ matrix.nimcpu }} \ | |
| --gcc.exe:arm-buildroot-linux-musleabi-gcc \ | |
| --gcc.linkerexe:arm-buildroot-linux-musleabi-gcc \ | |
| --passL:-static \ | |
| --passC:-O3 \ | |
| --passC:-march=armv5t \ | |
| --passC:-mfloat-abi=soft \ | |
| --passC:-mabi=aapcs-linux \ | |
| --passC:-ffunction-sections \ | |
| --passC:-fdata-sections \ | |
| --passC:-flto=auto --passL:-flto=auto \ | |
| --passL:-Wl,--gc-sections \ | |
| -o:bin/yabb src/yabb.nim | |
| arm-buildroot-linux-musleabi-strip --strip-all bin/yabb | |
| echo "Built: bin/yabb (${{ matrix.arch }})" | |
| - name: Verify binary | |
| run: | | |
| echo "=== bin/yabb (${{ matrix.arch }}) ===" | |
| file bin/yabb | |
| ls -lh bin/yabb | |
| if ldd bin/yabb 2>&1 | grep -qE "not a dynamic executable|statically linked"; then | |
| echo "OK: Statically linked" | |
| else | |
| echo "WARNING: May be dynamically linked" | |
| ldd bin/yabb || true | |
| fi | |
| - name: Prepare release archive | |
| run: | | |
| VERSION="${GITHUB_REF_NAME#v}" | |
| ARCHIVE_NAME="yabb-${VERSION}-linux-${{ matrix.arch }}" | |
| mkdir -p "${ARCHIVE_NAME}" | |
| # Copy binary | |
| cp bin/yabb "${ARCHIVE_NAME}/" | |
| # Create shell_completions directory and copy completions | |
| mkdir -p "${ARCHIVE_NAME}/shell_completions" | |
| cp completions/yabb.bash "${ARCHIVE_NAME}/shell_completions/" | |
| cp completions/yabb.fish "${ARCHIVE_NAME}/shell_completions/" | |
| cp completions/yabb.zsh "${ARCHIVE_NAME}/shell_completions/" | |
| # Create scripts directory and copy scripts | |
| mkdir -p "${ARCHIVE_NAME}/scripts" | |
| cp scripts/setup-yabb.sh "${ARCHIVE_NAME}/scripts/" | |
| chmod +x "${ARCHIVE_NAME}/scripts/setup-yabb.sh" | |
| cp scripts/yabb.service "${ARCHIVE_NAME}/scripts/" | |
| cp scripts/yabb.timer "${ARCHIVE_NAME}/scripts/" | |
| # Create symlink for setup-yabb.sh at root | |
| ln -s scripts/setup-yabb.sh "${ARCHIVE_NAME}/setup-yabb.sh" | |
| # Copy and rename sample config | |
| cp config/yabb.toml.example "${ARCHIVE_NAME}/yabb.conf" | |
| # Copy installation instructions | |
| cp Install.txt "${ARCHIVE_NAME}/" | |
| # Create tarball with zstd compression (level 19 = highest standard) | |
| tar -cvf - "${ARCHIVE_NAME}" | zstd -19 -o "${ARCHIVE_NAME}.tar.zst" | |
| # List contents for verification | |
| echo "" | |
| echo "Archive contents:" | |
| zstd -d -c "${ARCHIVE_NAME}.tar.zst" | tar -tvf - | |
| # Output for subsequent steps | |
| echo "ARCHIVE_NAME=${ARCHIVE_NAME}" >> $GITHUB_ENV | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.ARCHIVE_NAME }} | |
| path: ${{ env.ARCHIVE_NAME }}.tar.zst | |
| retention-days: 1 | |
| # ========================================================================== | |
| # Release - Collect all binaries and publish | |
| # ========================================================================== | |
| release: | |
| name: Create Release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| pattern: yabb-* | |
| merge-multiple: true | |
| - name: Generate checksums | |
| run: | | |
| cd artifacts | |
| sha256sum *.tar.zst > SHA256SUMS | |
| echo "Generated checksums:" | |
| cat SHA256SUMS | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| artifacts/*.tar.zst | |
| artifacts/SHA256SUMS | |
| generate_release_notes: true | |
| draft: false | |
| prerelease: ${{ contains(github.ref_name, '-') }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |