name: build on: push: branches: [main] tags: ['v*'] pull_request: workflow_dispatch: inputs: unikraft_ref: description: 'Unikraft tag/branch to build against' required: false default: RELEASE-0.21.0 permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref_type != 'tag' }} env: # The loader is a drop-in for app-elfloader, so it is pinned to a released # Unikraft rather than staging. Bump both together: the libs carry the same # release tags as the core. UNIKRAFT_REF: ${{ github.event.inputs.unikraft_ref || 'RELEASE-0.21.0' }} LWIP_REF: RELEASE-0.21.0 jobs: # The loader's own tests: pure logic, host triple, no Unikraft tree. test: name: cargo test runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Install Rust toolchain run: | rustup toolchain install stable --profile minimal rustup default stable - name: Test run: make test build: name: ${{ matrix.defconfig }} runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: include: - defconfig: qemu-x86_64-initrd arch: x86_64 rust_target: x86_64-unknown-none - defconfig: qemu-x86_64-9pfs arch: x86_64 rust_target: x86_64-unknown-none - defconfig: fc-x86_64-initrd arch: x86_64 rust_target: x86_64-unknown-none - defconfig: qemu-aarch64-9pfs arch: arm64 rust_target: aarch64-unknown-none-softfloat cross_compile: aarch64-linux-gnu- apt_extra: gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu steps: - uses: actions/checkout@v4 - name: Install build dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential libncurses-dev libyaml-dev flex bison \ wget unzip uuid-runtime python3 ${{ matrix.apt_extra }} # Unikraft invokes cargo from its own build directory, so rust/'s # rust-toolchain.toml is not in scope there: the bare-metal target has to # be present on the *default* toolchain, not just the toolchain-file one. - name: Install Rust toolchain run: | rustup toolchain install stable --profile minimal \ --target ${{ matrix.rust_target }} rustup default stable - name: Fetch Unikraft run: | git clone --depth 1 --branch "$UNIKRAFT_REF" \ https://github.com/unikraft/unikraft workdir/unikraft git clone --depth 1 --branch "$LWIP_REF" \ https://github.com/unikraft/lib-lwip workdir/libs/lwip # Upstream bug, live in RELEASE-0.21.0 and staging alike: # lib/syscall_shim/arch/arm64/syscall_handler.c dereferences # `struct ukarch_execenv` while including only , so any # arm64 build with the syscall handler enabled fails to compile. This is # the same one-line fix bsdkrun's unikraft-base/patches/apply.sh applies; # it turns into a no-op the moment upstream carries the include. - name: Patch Unikraft (arm64 syscall_shim include) if: matrix.arch == 'arm64' run: | f=workdir/unikraft/lib/syscall_shim/arch/arm64/syscall_handler.c if grep -q 'uk/arch/ctx.h' "$f"; then echo "$f already includes -- upstream fixed, skipping" else sed -i 's|#include |#include \n#include |' "$f" grep -q 'uk/arch/ctx.h' "$f" || { echo "patch did not apply"; exit 1; } echo "patched $f" fi - name: Configure run: | make defconfig \ ARCH=${{ matrix.arch }} \ CROSS_COMPILE=${{ matrix.cross_compile }} \ UK_DEFCONFIG="$PWD/defconfigs/${{ matrix.defconfig }}" - name: Build run: | make -j"$(nproc)" \ ARCH=${{ matrix.arch }} \ CROSS_COMPILE=${{ matrix.cross_compile }} # Unikraft names the image after the VMM, not the platform: # elfloader_qemu-x86_64, elfloader_fc-x86_64, elfloader_qemu-arm64. Glob # for it rather than spelling it out, and insist on exactly one match so # a naming change fails here instead of silently shipping the wrong file. # The defconfig goes into the released name so the images do not collide # and it stays obvious which rootfs a given image expects. - name: Collect image run: | shopt -s nullglob imgs=(workdir/build/elfloader_*-${{ matrix.arch }}) if [ ${#imgs[@]} -ne 1 ]; then echo "expected exactly one image, found: ${imgs[*]:-none}" ls -l workdir/build/ | grep elfloader || true exit 1 fi mkdir -p dist cp "${imgs[0]}" "dist/elfloader_${{ matrix.defconfig }}" cp "${imgs[0]}.dbg" "dist/elfloader_${{ matrix.defconfig }}.dbg" ls -l dist - uses: actions/upload-artifact@v4 with: name: elfloader_${{ matrix.defconfig }} path: dist/ if-no-files-found: error release: name: release needs: [test, build] if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-24.04 permissions: contents: write steps: - uses: actions/download-artifact@v4 with: path: dist merge-multiple: true - name: Checksums working-directory: dist run: | sha256sum elfloader_* > SHA256SUMS cat SHA256SUMS - name: Publish env: GH_TOKEN: ${{ github.token }} run: | gh release create "$GITHUB_REF_NAME" \ --repo "$GITHUB_REPOSITORY" \ --title "$GITHUB_REF_NAME" \ --generate-notes \ dist/*