130 lines
3.6 KiB
Bash
Executable file
130 lines
3.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "${root}"
|
|
|
|
forge_host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
|
|
forge_repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
|
|
release_tag="${EVERY_CHANNEL_NETBOOT_RELEASE_TAG:-}"
|
|
local_tarball="${EVERY_CHANNEL_NETBOOT_TARBALL:-}"
|
|
out_root="${EVERY_CHANNEL_NETBOOT_ROOT:-tmp/netboot}"
|
|
ipxe_efi_url="${EVERY_CHANNEL_IPXE_EFI_URL:-https://boot.ipxe.org/snponly.efi}"
|
|
netboot_hostname="${EVERY_CHANNEL_NETBOOT_HOSTNAME:-boot.every.channel}"
|
|
http_port="${EVERY_CHANNEL_NETBOOT_HTTP_PORT:-8080}"
|
|
token="${EVERY_CHANNEL_FORGE_TOKEN:-${FORGE_TOKEN:-${CODEBERG_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
|
|
}
|
|
|
|
need_cmd curl
|
|
need_cmd tar
|
|
need_cmd python3
|
|
|
|
tmp_dir="$(mktemp -d)"
|
|
cleanup() {
|
|
rm -rf "${tmp_dir}"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
archive_path="${tmp_dir}/netboot.tar.gz"
|
|
release_asset_url=""
|
|
|
|
if [[ -n "${local_tarball}" ]]; then
|
|
if [[ ! -f "${local_tarball}" ]]; then
|
|
echo "error: netboot tarball not found: ${local_tarball}" >&2
|
|
exit 2
|
|
fi
|
|
cp -f "${local_tarball}" "${archive_path}"
|
|
else
|
|
api_base="${forge_host%/}/api/v1/repos/${forge_repo}"
|
|
release_endpoint="${api_base}/releases/latest"
|
|
if [[ -n "${release_tag}" ]]; then
|
|
release_endpoint="${api_base}/releases/tags/${release_tag}"
|
|
fi
|
|
|
|
auth_args=()
|
|
if [[ -n "${token}" ]]; then
|
|
auth_args=(-H "Authorization: token ${token}")
|
|
fi
|
|
|
|
release_json="${tmp_dir}/release.json"
|
|
curl -fsSL "${auth_args[@]}" "${release_endpoint}" -o "${release_json}"
|
|
|
|
release_asset_url="$(
|
|
python3 - "${release_json}" <<'PY'
|
|
import json
|
|
import sys
|
|
|
|
path = sys.argv[1]
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
assets = data.get("assets", [])
|
|
candidates = []
|
|
for asset in assets:
|
|
name = asset.get("name", "")
|
|
if name.startswith("ec-runner-x86_64-netboot-") and name.endswith(".tar.gz"):
|
|
candidates.append(asset)
|
|
|
|
if not candidates:
|
|
sys.exit(1)
|
|
|
|
# Pick newest by release ordering if API already sorted; otherwise prefer largest id.
|
|
chosen = sorted(candidates, key=lambda x: x.get("id", 0))[-1]
|
|
print(chosen.get("browser_download_url", ""))
|
|
PY
|
|
)"
|
|
|
|
if [[ -z "${release_asset_url}" ]]; then
|
|
echo "error: unable to find x86_64 netboot asset in release" >&2
|
|
exit 2
|
|
fi
|
|
|
|
curl -fsSL "${auth_args[@]}" -o "${archive_path}" "${release_asset_url}"
|
|
fi
|
|
|
|
http_dir="${out_root}/http"
|
|
tftp_dir="${out_root}/tftp"
|
|
rm -rf "${http_dir}"
|
|
mkdir -p "${http_dir}" "${tftp_dir}"
|
|
|
|
tar -xzf "${archive_path}" -C "${http_dir}"
|
|
|
|
for required in kernel initrd netboot.ipxe; do
|
|
if [[ ! -f "${http_dir}/${required}" ]]; then
|
|
echo "error: extracted netboot bundle is missing ${required}" >&2
|
|
exit 2
|
|
fi
|
|
done
|
|
|
|
curl -fsSL -o "${tftp_dir}/ipxe.efi" "${ipxe_efi_url}"
|
|
cp -f "${http_dir}/netboot.ipxe" "${tftp_dir}/netboot.ipxe"
|
|
|
|
cat > "${tftp_dir}/bootstrap.ipxe" <<'EOF'
|
|
#!ipxe
|
|
dhcp
|
|
chain http://__NETBOOT_HOST__:__HTTP_PORT__/netboot.ipxe
|
|
EOF
|
|
sed -i.bak \
|
|
-e "s#__NETBOOT_HOST__#${netboot_hostname}#g" \
|
|
-e "s#__HTTP_PORT__#${http_port}#g" \
|
|
"${tftp_dir}/bootstrap.ipxe"
|
|
rm -f "${tftp_dir}/bootstrap.ipxe.bak"
|
|
|
|
echo "ok: staged netboot content"
|
|
echo "ok: http root: ${http_dir}"
|
|
echo "ok: tftp root: ${tftp_dir}"
|
|
echo "ok: netboot hostname: ${netboot_hostname}"
|
|
echo "ok: netboot http port: ${http_port}"
|
|
if [[ -n "${release_asset_url}" ]]; then
|
|
echo "ok: source asset: ${release_asset_url}"
|
|
else
|
|
echo "ok: source asset: ${local_tarball}"
|
|
fi
|
|
echo "hint: run sudo ./scripts/netboot-serve.sh to expose HTTP+TFTP+ProxyDHCP"
|