diff --git a/.gitignore b/.gitignore index 46aaaaa..24a6377 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ +# Build artifacts and downloads +artifacts/ +*.iso +*.sha256.txt +*.zip + +# OS / editor +.DS_Store +Thumbs.db +.vscode/ +node_modules/ node_modules dist .DS_Store diff --git a/docs/FLASH_USB.md b/docs/FLASH_USB.md new file mode 100644 index 0000000..01d5c33 --- /dev/null +++ b/docs/FLASH_USB.md @@ -0,0 +1,42 @@ +# Flash AeThex OS ISO to USB + +This guide shows how to write the AeThex OS ISO to a USB drive on Linux, macOS, and Windows. + +## Linux/macOS (Script) + +- Prereqs: `sudo`, optional `pv` for progress. +- File: [script/flash-usb.sh](script/flash-usb.sh) + +Commands: + +```bash +sudo ./script/flash-usb.sh -i artifacts/AeThex-Linux-amd64.iso +# Or specify the device +sudo ./script/flash-usb.sh -i artifacts/AeThex-Linux-amd64.iso -d /dev/sdX +``` + +Notes: +- The script lists removable drives and prompts for confirmation. +- It unmounts any partitions and writes the ISO with `dd` (progress shown when `pv` is installed). + +## Windows (Rufus) + +Windows does not include a native `dd`. Use Rufus: +- Download Rufus: https://rufus.ie +- Insert USB drive. +- Select the ISO: `AeThex-Linux-amd64.iso`. +- Partition scheme: `GPT` for UEFI, `MBR` for BIOS. +- Hit Start and confirm. + +Optional PowerShell checks: + +```powershell +Get-Disk | Where-Object BusType -eq 'USB' +Get-Volume | Where-Object DriveType -eq 'Removable' +``` + +## Boot Tips + +- Prefer UEFI boot with Secure Boot disabled (or enroll keys if supported). +- If the system does not boot, try toggling CSM/Legacy mode. +- Use `F12/F10/ESC/DEL` (vendor dependent) to open the boot menu. diff --git a/script/flash-usb.sh b/script/flash-usb.sh new file mode 100755 index 0000000..4dd2553 --- /dev/null +++ b/script/flash-usb.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +set -euo pipefail + +# AeThex OS USB flashing helper (Linux/macOS) +# Usage: +# sudo ./script/flash-usb.sh -i path/to/AeThex-Linux-amd64.iso -d /dev/sdX +# sudo ./script/flash-usb.sh -i path/to/AeThex-Linux-amd64.iso # will list devices and prompt + +ISO="" +DEVICE="" + +usage() { + echo "Usage: sudo $0 -i [-d ]" >&2 + echo "Example: sudo $0 -i ./artifacts/AeThex-Linux-amd64.iso -d /dev/sdX" >&2 +} + +while getopts ":i:d:h" opt; do + case "$opt" in + i) ISO="$OPTARG" ;; + d) DEVICE="$OPTARG" ;; + h) usage; exit 0 ;; + *) usage; exit 1 ;; + esac +done + +if [[ $EUID -ne 0 ]]; then + echo "This script must run as root (use sudo)." >&2 + exit 1 +fi + +if [[ -z "$ISO" ]]; then + usage + exit 1 +fi + +if [[ ! -f "$ISO" ]]; then + echo "ISO not found: $ISO" >&2 + exit 1 +fi + +OS_NAME="$(uname -s)" + +list_devices_linux() { + lsblk -dpno NAME,SIZE,MODEL,TRAN | grep -E "/dev/" || true +} + +list_devices_macos() { + diskutil list | sed -n '/(external, physical)/,/^$/p' || diskutil list +} + +echo "ISO: $ISO" +echo "Detecting removable drives..." + +if [[ "$OS_NAME" == "Linux" ]]; then + list_devices_linux +elif [[ "$OS_NAME" == "Darwin" ]]; then + list_devices_macos +else + echo "Unsupported OS: $OS_NAME" >&2 + exit 1 +fi + +if [[ -z "$DEVICE" ]]; then + read -r -p "Enter target device (e.g., /dev/sdX or /dev/diskN): " DEVICE +fi + +if [[ -z "$DEVICE" ]]; then + echo "No device specified." >&2 + exit 1 +fi + +echo "\nWARNING: This will ERASE ALL DATA on $DEVICE" +read -r -p "Type ERASE to continue: " CONFIRM +if [[ "$CONFIRM" != "ERASE" ]]; then + echo "Aborted." >&2 + exit 1 +fi + +echo "Unmounting any mounted partitions from $DEVICE..." +if [[ "$OS_NAME" == "Linux" ]]; then + mapfile -t parts < <(lsblk -no NAME "$DEVICE" 2>/dev/null | tail -n +2) + for p in "${parts[@]}"; do + mountpoint="/dev/$p" + umount "$mountpoint" 2>/dev/null || true + done +elif [[ "$OS_NAME" == "Darwin" ]]; then + diskutil unmountDisk force "$DEVICE" || true +fi + +echo "Writing ISO to $DEVICE... this may take several minutes." +if [[ "$OS_NAME" == "Linux" ]]; then + if command -v pv >/dev/null 2>&1; then + pv "$ISO" | dd of="$DEVICE" bs=4M conv=fsync status=progress + else + dd if="$ISO" of="$DEVICE" bs=4M conv=fsync status=progress + fi + sync +elif [[ "$OS_NAME" == "Darwin" ]]; then + # On macOS, use raw disk for performance (/dev/rdiskN) + RAW_DEVICE="$DEVICE" + if [[ "$DEVICE" == /dev/disk* ]]; then + RAW_DEVICE="/dev/r$(basename "$DEVICE")" + fi + dd if="$ISO" of="$RAW_DEVICE" bs=4m + sync + diskutil eject "$DEVICE" || true +fi + +echo "\nDone. Safely remove the USB, plug into target PC, and boot." +echo "If boot fails on UEFI, ensure Secure Boot is disabled or keys enrolled." diff --git a/script/gitlab-download-artifacts.sh b/script/gitlab-download-artifacts.sh new file mode 100755 index 0000000..28a4f3b --- /dev/null +++ b/script/gitlab-download-artifacts.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +set -euo pipefail + +GITLAB_HOST="${GITLAB_HOST:-https://gitlab.com}" +API_BASE="$GITLAB_HOST/api/v4" + +PROJECT_PATH="${PROJECT_PATH:-}" +REF="${REF:-main}" +JOB_NAME="${JOB_NAME:-build_iso}" +PIPELINE_ID="${PIPELINE_ID:-}" +OUT_DIR="${OUT_DIR:-artifacts}" + +if [[ -z "${GITLAB_TOKEN:-}" ]]; then + echo "GITLAB_TOKEN is required" >&2 + exit 1 +fi + +if [[ -z "$PROJECT_PATH" ]]; then + echo "PROJECT_PATH is required (e.g., AeThex-Corporation/AeThex-OS)" >&2 + exit 1 +fi + +mkdir -p "$OUT_DIR" + +urlencode_project() { + echo -n "$PROJECT_PATH" | sed -e 's/\//%2F/g' +} + +PROJECT_ENC="$(urlencode_project)" + +auth() { + echo "PRIVATE-TOKEN: $GITLAB_TOKEN" +} + +get_json() { + local url="$1" + curl -sS -H "$(auth)" "$url" +} + +require_jq() { + if ! command -v jq >/dev/null 2>&1; then + echo "jq is required. Install with: sudo apt-get update && sudo apt-get install -y jq" >&2 + exit 1 + fi +} + +require_jq + +if [[ -z "$PIPELINE_ID" ]]; then + PIPELINE_ID=$(get_json "$API_BASE/projects/$PROJECT_ENC/pipelines?ref=$REF&status=success&order_by=updated_at&sort=desc&per_page=1" | jq -r '.[0].id') + if [[ -z "$PIPELINE_ID" || "$PIPELINE_ID" == "null" ]]; then + echo "No successful pipeline found for ref=$REF" >&2 + exit 1 + fi +fi + +JOBS_JSON=$(get_json "$API_BASE/projects/$PROJECT_ENC/pipelines/$PIPELINE_ID/jobs?scope=success") +JOB_ID=$(echo "$JOBS_JSON" | jq -r + --arg name "$JOB_NAME" + '[.[] | select(.name == $name and .artifacts_file and (.artifacts_file.filename != null))][0].id') + +if [[ -z "$JOB_ID" || "$JOB_ID" == "null" ]]; then + echo "No job with artifacts found matching name=$JOB_NAME in pipeline=$PIPELINE_ID" >&2 + echo "Available job names:" >&2 + echo "$JOBS_JSON" | jq -r '.[].name' >&2 + exit 1 +fi + +ART_ZIP="$OUT_DIR/${PIPELINE_ID}-${JOB_NAME}.zip" +echo "Downloading artifacts from job $JOB_ID to $ART_ZIP" +curl -fSL -H "$(auth)" "$API_BASE/projects/$PROJECT_ENC/jobs/$JOB_ID/artifacts" -o "$ART_ZIP" + +echo "Extracting $ART_ZIP" +unzip -o "$ART_ZIP" -d "$OUT_DIR" >/dev/null + +ISO_PATH=$(find "$OUT_DIR" -type f -name '*.iso' | head -n 1 || true) +if [[ -n "$ISO_PATH" ]]; then + echo "Found ISO: $ISO_PATH" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$ISO_PATH" | tee "$ISO_PATH.sha256.txt" + fi +else + echo "No ISO file found in artifacts. Contents:" >&2 + find "$OUT_DIR" -maxdepth 2 -type f -printf '%p\n' >&2 +fi + +echo "Done. Artifacts in $OUT_DIR"