#!/usr/bin/env bash set -euo pipefail root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${root}" netboot_root="${EVERY_CHANNEL_NETBOOT_ROOT:-tmp/netboot}" tftp_dir="${netboot_root}/tftp" netboot_hostname="${EVERY_CHANNEL_NETBOOT_HOSTNAME:-boot.every.channel}" http_port="${EVERY_CHANNEL_NETBOOT_HTTP_PORT:-8080}" ipxe_repo="${EVERY_CHANNEL_NETBOOT_IPXE_REPO:-https://github.com/ipxe/ipxe.git}" ipxe_ref="${EVERY_CHANNEL_NETBOOT_IPXE_REF:-}" output_name="${EVERY_CHANNEL_NETBOOT_IPXE_FILENAME:-ec-ipxe.efi}" use_docker="${EVERY_CHANNEL_NETBOOT_IPXE_USE_DOCKER:-auto}" docker_image="${EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_IMAGE:-ubuntu:24.04}" docker_platform="${EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_PLATFORM:-linux/amd64}" chain_token="${EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN:-}" need_cmd() { local name="$1" if ! command -v "${name}" >/dev/null 2>&1; then echo "error: required command not found: ${name}" >&2 exit 2 fi } validate_chain_token() { local value="$1" if [[ -z "${value}" ]]; then return 0 fi if [[ ! "${value}" =~ ^[A-Za-z0-9._~-]{16,128}$ ]]; then echo "error: EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN must match [A-Za-z0-9._~-]{16,128}" >&2 exit 2 fi } validate_chain_token "${chain_token}" tmp_dir="$(mktemp -d)" cleanup() { rm -rf "${tmp_dir}" } trap cleanup EXIT repo_dir="${tmp_dir}/ipxe" embed_script="${tmp_dir}/embed.ipxe" build_output="${tmp_dir}/ipxe.efi" chain_url="http://${netboot_hostname}:${http_port}/netboot.ipxe" if [[ -n "${chain_token}" ]]; then chain_url="${chain_url}?token=${chain_token}" fi cat > "${embed_script}" <&2 exit 2 ;; esac } build_native() { need_cmd git need_cmd make if ! command -v cc >/dev/null 2>&1 && ! command -v gcc >/dev/null 2>&1; then echo "error: required C compiler not found (need cc or gcc)" >&2 return 1 fi if ! command -v objcopy >/dev/null 2>&1; then echo "error: required tool not found: objcopy" >&2 return 1 fi git clone --depth 1 "${ipxe_repo}" "${repo_dir}" >/dev/null if [[ -n "${ipxe_ref}" ]]; then git -C "${repo_dir}" fetch --depth 1 origin "${ipxe_ref}" >/dev/null git -C "${repo_dir}" checkout -q FETCH_HEAD fi make -C "${repo_dir}/src" -s "bin-x86_64-efi/ipxe.efi" "EMBED=${embed_script}" cp -f "${repo_dir}/src/bin-x86_64-efi/ipxe.efi" "${build_output}" } build_docker() { need_cmd docker docker run --rm \ --platform "${docker_platform}" \ -v "${tmp_dir}:/work" \ -e IPXE_REPO="${ipxe_repo}" \ -e IPXE_REF="${ipxe_ref}" \ -w /work \ "${docker_image}" \ bash -lc ' set -euo pipefail apt-get update >/dev/null DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ ca-certificates git make gcc binutils perl mtools >/dev/null git clone --depth 1 "${IPXE_REPO}" ipxe >/dev/null if [ -n "${IPXE_REF}" ]; then git -C ipxe fetch --depth 1 origin "${IPXE_REF}" >/dev/null git -C ipxe checkout -q FETCH_HEAD fi make -C ipxe/src -s "bin-x86_64-efi/ipxe.efi" "EMBED=/work/embed.ipxe" ' cp -f "${tmp_dir}/ipxe/src/bin-x86_64-efi/ipxe.efi" "${build_output}" } use_docker="$(bool_norm "${use_docker}")" build_ok="false" if [[ "${use_docker}" != "true" ]]; then if build_native; then build_ok="true" fi fi if [[ "${build_ok}" != "true" ]]; then if [[ "${use_docker}" == "false" ]]; then exit 2 fi build_docker fi mkdir -p "${tftp_dir}" cp -f "${build_output}" "${tftp_dir}/${output_name}" if command -v sha256sum >/dev/null 2>&1; then sha256sum "${tftp_dir}/${output_name}" > "${tftp_dir}/${output_name}.sha256" elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "${tftp_dir}/${output_name}" > "${tftp_dir}/${output_name}.sha256" fi echo "ok: built embedded iPXE binary: ${tftp_dir}/${output_name}" echo "ok: chains to ${chain_url}" if [[ -n "${chain_token}" ]]; then echo "ok: chain token enabled" fi echo "hint: in UniFi set DHCP boot filename to ${output_name}"