Skip to content

bugs

bugs #9

Workflow file for this run

name: build-siboot
# Cross-compiles `ish-bootstrap/` to a static i686-unknown-linux-musl binary and
# attaches it to a release as `siboot-i686` — the file the install instructions
# tell people to curl onto their phone.
#
# Why a container rather than `cross`: siboot depends on
# `rust-core/vendor/idevice` by path, which sits outside its own cargo
# workspace. `cross` mounts the workspace root and would leave that path
# unmounted; a container checks the whole repository out, so the path resolves
# the way it does locally.
#
# The image supplies the one thing ubuntu-latest cannot: an i686-linux-musl C
# toolchain. Two dependencies compile C — `ring` (its 32-bit x86 assembly, all
# pregenerated, so no Perl is needed) and `liblzma-sys` (reached through
# apple-codesign's xar support, which nothing here calls but which is not
# feature-gated upstream). Its bindings are pregenerated too, so no libclang.
on:
workflow_dispatch:
inputs:
attach_to_release:
description: "Upload siboot-i686 to the latest release"
required: false
type: boolean
default: false
push:
paths:
- "ish-bootstrap/**"
- ".github/workflows/build-siboot.yml"
release:
types: [published]
permissions:
contents: write # attach the binary to the release
jobs:
build:
runs-on: ubuntu-latest
container:
image: messense/rust-musl-cross:i686-musl
steps:
- name: Checkout repository
uses: actions/checkout@v7
- name: Check the toolchain is new enough
# `idevice` and `isideload` are both edition 2024, which needs 1.85 or
# newer. Checked rather than updated: this image ships a damaged
# component manifest, and `rustup update stable` dies trying to remove
# the old clippy —
# error: failure removing component 'clippy-preview-...',
# directory does not exist: 'share/doc/clippy/README.md'
# — then rolls the whole update back. Adding a target does not touch
# components, so it stays safe.
run: |
set -eu
rustc --version
MIN_MINOR=85
have=$(rustc --version | sed -n 's/^rustc 1\.\([0-9][0-9]*\).*/\1/p')
if [ -z "$have" ] || [ "$have" -lt "$MIN_MINOR" ]; then
echo "::warning::rustc is older than 1.$MIN_MINOR; replacing the toolchain"
# Uninstall-then-install rather than update: removing the whole
# toolchain directory avoids the per-component path the bug is in.
rustup toolchain uninstall stable || true
rustup toolchain install stable --profile minimal
rustup default stable
fi
if ! rustup target list --installed | grep -qx i686-unknown-linux-musl; then
rustup target add i686-unknown-linux-musl
fi
rustc --version
cargo --version
rustup target list --installed
- name: Cache cargo registry and build
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
ish-bootstrap/target
key: siboot-${{ runner.os }}-${{ hashFiles('ish-bootstrap/Cargo.toml', 'ish-bootstrap/Cargo.lock') }}
restore-keys: siboot-${{ runner.os }}-
- name: Build siboot
working-directory: ish-bootstrap
run: cargo build --release --target i686-unknown-linux-musl
- name: Verify the binary
working-directory: ish-bootstrap
run: |
BIN=target/i686-unknown-linux-musl/release/siboot
file "$BIN"
ls -lh "$BIN"
# Everything below is a hard requirement of running inside iSH, so a
# regression should fail the build rather than reach a phone.
file "$BIN" | grep -q "ELF 32-bit LSB" || { echo "::error::not a 32-bit ELF"; exit 1; }
file "$BIN" | grep -q "Intel 80386" || { echo "::error::not an i386 binary"; exit 1; }
file "$BIN" | grep -q "statically linked" || { echo "::error::not statically linked"; exit 1; }
# A dynamic loader entry would mean iSH's Alpine has to supply a
# matching libc, which is exactly what static linking avoids.
READELF=$(command -v readelf || command -v i686-linux-musl-readelf || true)
if [ -n "$READELF" ]; then
if "$READELF" -l "$BIN" | grep -q "Requesting program interpreter"; then
echo "::error::binary requests a dynamic loader"; exit 1
fi
else
echo "::warning::no readelf available; skipped the dynamic-loader check"
fi
echo "size=$(stat -c %s "$BIN") bytes" >> "$GITHUB_STEP_SUMMARY"
- name: Stage as siboot-i686
working-directory: ish-bootstrap
run: cp target/i686-unknown-linux-musl/release/siboot siboot-i686
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: siboot-i686
path: ish-bootstrap/siboot-i686
if-no-files-found: error
- name: Resolve the release tag
# A push to the default branch attaches too, not just a published
# release. The install instructions hardcode
# `releases/latest/download/siboot-i686`, so that asset *is* the
# distribution — leaving it to a manual tick means every change ships
# only if someone remembers to tick it. Branch pushes and pull requests
# still build without touching any release.
id: tag
if: >-
github.event_name == 'release' ||
inputs.attach_to_release ||
(github.event_name == 'push' &&
github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ github.event.release.tag_name }}"
if [ -z "$TAG" ]; then
# Manual run: aim at the latest published release, the same one the
# install instructions point at.
TAG=$(curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/releases/latest" \
| sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -1)
fi
if [ -z "$TAG" ]; then
echo "::error::no release to attach to"; exit 1
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "attaching to $TAG" >> "$GITHUB_STEP_SUMMARY"
- name: Attach to release
if: steps.tag.outputs.tag != ''
uses: softprops/action-gh-release@v2
with:
files: ish-bootstrap/siboot-i686
tag_name: ${{ steps.tag.outputs.tag }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}