diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml new file mode 100644 index 0000000..d480e27 --- /dev/null +++ b/.github/workflows/build-image.yml @@ -0,0 +1,343 @@ +name: Build and Release Disk Image + +on: + push: + branches: + - main + - develop + tags: + - 'v*' + pull_request: + branches: + - main + - develop + workflow_dispatch: + +jobs: + build-image: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build disk image using Docker + run: | + echo "Building SparkOS disk image..." + # Build the image builder container + docker buildx build \ + --file Dockerfile.image \ + --target image-builder \ + --tag sparkos:image-builder \ + --load \ + . + + # Create a container and extract the image + echo "Extracting disk image..." + CONTAINER_ID=$(docker create sparkos:image-builder) + docker cp "$CONTAINER_ID:/output/sparkos.img.gz" ./sparkos.img.gz + docker rm "$CONTAINER_ID" + + echo "Disk image created successfully!" + ls -lh sparkos.img.gz + + - name: Verify disk image + run: | + echo "Verifying UEFI-bootable disk image..." + if [ ! -f sparkos.img.gz ]; then + echo "ERROR: Disk image not found!" + exit 1 + fi + + # Show file info + ls -lh sparkos.img.gz + file sparkos.img.gz + + # Decompress and check + echo "Decompressing to verify..." + gunzip -c sparkos.img.gz > sparkos.img + ls -lh sparkos.img + file sparkos.img + + # Show partition table (should be GPT) + echo "" + echo "=== Partition Table (GPT) ===" + sudo fdisk -l sparkos.img || true + sudo parted sparkos.img print || true + + # Try to mount and inspect partitions + echo "" + echo "=== Inspecting Partitions ===" + mkdir -p /tmp/sparkos_esp /tmp/sparkos_root + + # Set up loop device + LOOP_DEV=$(sudo losetup -fP --show sparkos.img) + echo "Loop device: $LOOP_DEV" + ls -la ${LOOP_DEV}* + + # Mount and check ESP + if sudo mount -o ro ${LOOP_DEV}p1 /tmp/sparkos_esp 2>/dev/null; then + echo "✓ EFI System Partition mounted" + echo "ESP contents:" + ls -laR /tmp/sparkos_esp/ + if [ -f /tmp/sparkos_esp/EFI/BOOT/BOOTX64.EFI ]; then + echo "✓ UEFI bootloader (GRUB) found" + fi + if [ -f /tmp/sparkos_esp/boot/vmlinuz ]; then + echo "✓ Kernel found" + fi + sudo umount /tmp/sparkos_esp + fi + + # Mount and check root partition + if sudo mount -o ro ${LOOP_DEV}p2 /tmp/sparkos_root 2>/dev/null; then + echo "✓ Root partition mounted" + echo "Root contents:" + ls -la /tmp/sparkos_root/ + if [ -f /tmp/sparkos_root/sbin/init ]; then + echo "✓ Init binary found" + ls -lh /tmp/sparkos_root/sbin/init + fi + if [ -f /tmp/sparkos_root/bin/busybox ]; then + echo "✓ Busybox found" + fi + sudo umount /tmp/sparkos_root + fi + + # Cleanup + sudo losetup -d $LOOP_DEV + + echo "" + echo "=== Verification Complete ===" + echo "✓ UEFI-bootable image with GPT partition table" + echo "✓ EFI System Partition with GRUB" + echo "✓ Root partition with init system and busybox" + + - name: Create release package + run: | + echo "Creating release package..." + mkdir -p release + + # Move the compressed image + mv sparkos.img.gz release/ + + # Create a README for the release + cat > release/README.txt << 'EOF' + SparkOS UEFI-Bootable Disk Image + ================================== + + This package contains a UEFI-bootable disk image with SparkOS. + + Files: + - sparkos.img.gz - Compressed UEFI-bootable disk image (~1GB) + + What's Included: + --------------- + ✓ GPT partition table + ✓ EFI System Partition (ESP) with FAT32 filesystem + ✓ GRUB UEFI bootloader + ✓ Linux kernel + ✓ SparkOS init system + ✓ Busybox utilities + ✓ Basic FHS-compliant filesystem structure + + Boot Support: + ------------ + ✓ UEFI boot (x86_64 systems) + ✓ Automatic boot after 3 seconds + ✓ Console on tty1 + + Quick Start: + ----------- + + 1. Decompress the image: + gunzip sparkos.img.gz + + 2. Write to USB drive (Linux - BE CAREFUL!): + sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress oflag=sync + + WARNING: Replace /dev/sdX with your actual USB drive device. + This will DESTROY all data on the target drive! + + 3. Boot from USB: + - Insert the USB drive into a UEFI-capable system + - Enter BIOS/UEFI settings (usually F2, F12, DEL, or ESC at boot) + - Select the USB drive as boot device + - SparkOS should boot automatically + + Advanced - Inspect Partitions: + ------------------------------ + + To mount and inspect the partitions: + + # Set up loop device + sudo losetup -fP --show sparkos.img + # Note the loop device name (e.g., /dev/loop0) + + # Mount ESP (EFI System Partition) + sudo mount /dev/loop0p1 /mnt + ls -la /mnt/EFI + sudo umount /mnt + + # Mount root partition + sudo mount /dev/loop0p2 /mnt + ls -la /mnt + sudo umount /mnt + + # Cleanup + sudo losetup -d /dev/loop0 + + Documentation: + ------------- + See the full documentation at: + https://github.com/johndoe6345789/SparkOS + + Support: + ------- + For issues and questions, visit: + https://github.com/johndoe6345789/SparkOS/issues + EOF + + # Create a ZIP with the image and README + cd release || exit 1 + zip sparkos-image.zip sparkos.img.gz README.txt + cd .. || exit 1 + + echo "Release package created!" + ls -lh release/ + + - name: Upload image artifact + uses: actions/upload-artifact@v4 + with: + name: sparkos-image-${{ github.sha }} + path: | + release/sparkos-image.zip + release/sparkos.img.gz + retention-days: 90 + + - name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: | + release/sparkos-image.zip + release/sparkos.img.gz + body: | + # SparkOS UEFI-Bootable Disk Image Release ${{ github.ref_name }} + + This release includes a **UEFI-bootable** disk image with SparkOS. + + ## What's Included + + - **sparkos-image.zip**: Complete package with image and README + - **sparkos.img.gz**: Compressed UEFI-bootable disk image (~1GB) + + ## Features + + ✅ **UEFI Boot Support** - Boot on modern UEFI systems + ✅ **GPT Partition Table** - Modern partitioning scheme + ✅ **GRUB Bootloader** - Reliable UEFI bootloader + ✅ **Linux Kernel** - Full kernel included + ✅ **SparkOS Init System** - Custom init with busybox + ✅ **Ready to Boot** - Write to USB and boot immediately + + ## Quick Start + + ### Write to USB and Boot + + ```bash + # Download and decompress + wget https://github.com/johndoe6345789/SparkOS/releases/download/${{ github.ref_name }}/sparkos.img.gz + gunzip sparkos.img.gz + + # Write to USB drive (Linux - BE CAREFUL!) + sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress oflag=sync + ``` + + **⚠️ WARNING**: Replace `/dev/sdX` with your actual USB device (e.g., `/dev/sdb`). This will **ERASE ALL DATA** on the target drive! + + ### Boot Instructions + + 1. Insert the USB drive into a UEFI-capable system + 2. Enter BIOS/UEFI settings (usually F2, F12, DEL, or ESC at boot) + 3. Select the USB drive as boot device + 4. SparkOS will boot automatically after 3 seconds + + ### Inspect Partitions (Advanced) + + ```bash + # Set up loop device + LOOP_DEV=$(sudo losetup -fP --show sparkos.img) + + # Mount ESP (EFI System Partition) + sudo mount ${LOOP_DEV}p1 /mnt + ls -la /mnt/EFI # View bootloader and kernel + sudo umount /mnt + + # Mount root partition + sudo mount ${LOOP_DEV}p2 /mnt + ls -la /mnt # View SparkOS filesystem + sudo umount /mnt + + # Cleanup + sudo losetup -d $LOOP_DEV + ``` + + ## Technical Details + + - **Size**: ~1GB (compressed) + - **Partition Table**: GPT + - **ESP**: 200MB FAT32 (EFI System Partition) + - **Root**: ~800MB ext4 + - **Bootloader**: GRUB (UEFI) + - **Kernel**: Linux kernel (from Ubuntu) + - **Init**: SparkOS custom init system + - **Shell**: Busybox + + ## Docker Image + + A Docker image is also available: + ```bash + docker pull ghcr.io/johndoe6345789/sparkos:${{ github.ref_name }} + ``` + + ## Documentation + + See [README.md](https://github.com/johndoe6345789/SparkOS/blob/main/README.md) for full documentation. + + ## What's Changed + + See commit history for detailed changes. + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Output summary + run: | + echo "### Disk Image Build Summary 💾" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** ✅ Success" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Disk Image:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ls -lh release/sparkos.img.gz >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Release Package:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ls -lh release/sparkos-image.zip >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + echo "**Release:** Created GitHub release for ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Download from: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY + else + echo "**Artifact:** Uploaded as workflow artifact (available for 90 days)" >> $GITHUB_STEP_SUMMARY + fi diff --git a/Dockerfile.image b/Dockerfile.image new file mode 100644 index 0000000..00bd79d --- /dev/null +++ b/Dockerfile.image @@ -0,0 +1,179 @@ +# Dockerfile for building UEFI-bootable SparkOS image +# Creates a .img file with GPT partition table, ESP, and GRUB + +FROM ubuntu:22.04 AS image-builder + +# Install required tools +RUN apt-get update && \ + apt-get install -y \ + gcc \ + make \ + dosfstools \ + mtools \ + e2fsprogs \ + parted \ + gdisk \ + grub-efi-amd64-bin \ + grub-common \ + wget \ + busybox-static \ + kmod \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +# Copy source files +COPY src/ ./src/ +COPY Makefile . +COPY scripts/ ./scripts/ +COPY config/ ./config/ +COPY rootfs/ ./rootfs/ + +# Build the init binary +RUN make init + +# Download a minimal Linux kernel (using Ubuntu's kernel for now) +RUN mkdir -p /kernel && \ + apt-get update && \ + apt-get download linux-image-generic && \ + dpkg -x linux-image-*.deb /kernel && \ + rm -rf /var/lib/apt/lists/* linux-image-*.deb + +# Create UEFI-bootable image with GPT partition table +RUN mkdir -p /output /mnt/esp /mnt/root && \ + echo "=== Creating UEFI-bootable SparkOS image with GRUB ===" && \ + # Create 1GB disk image (larger for kernel + bootloader) + dd if=/dev/zero of=/output/sparkos.img bs=1M count=1024 && \ + \ + # Create GPT partition table + echo "Creating GPT partition table..." && \ + parted -s /output/sparkos.img mklabel gpt && \ + \ + # Create EFI System Partition (ESP) - 200MB, FAT32 + echo "Creating EFI System Partition..." && \ + parted -s /output/sparkos.img mkpart ESP fat32 1MiB 201MiB && \ + parted -s /output/sparkos.img set 1 esp on && \ + \ + # Create root partition - remaining space, ext4 + echo "Creating root partition..." && \ + parted -s /output/sparkos.img mkpart primary ext4 201MiB 100% && \ + \ + # Set up loop device for the image + LOOP_DEV=$(losetup -f) && \ + losetup -P $LOOP_DEV /output/sparkos.img && \ + \ + # Wait for partition devices + sleep 1 && \ + \ + # Format partitions + echo "Formatting EFI System Partition (FAT32)..." && \ + mkfs.vfat -F 32 -n "SPARKOSEFI" ${LOOP_DEV}p1 && \ + \ + echo "Formatting root partition (ext4)..." && \ + mkfs.ext4 -L "SparkOS" ${LOOP_DEV}p2 && \ + \ + # Mount ESP + echo "Mounting partitions..." && \ + mount ${LOOP_DEV}p1 /mnt/esp && \ + mount ${LOOP_DEV}p2 /mnt/root && \ + \ + # Install GRUB to ESP + echo "Installing GRUB bootloader..." && \ + mkdir -p /mnt/esp/EFI/BOOT && \ + \ + # Create GRUB EFI binary using grub-mkstandalone + grub-mkstandalone \ + --format=x86_64-efi \ + --output=/mnt/esp/EFI/BOOT/BOOTX64.EFI \ + --locales="" \ + --fonts="" \ + "boot/grub/grub.cfg=/dev/null" && \ + \ + # Find the kernel + KERNEL_PATH=$(find /kernel/boot -name "vmlinuz-*" | head -1) && \ + KERNEL_VERSION=$(basename $KERNEL_PATH | sed 's/vmlinuz-//') && \ + INITRD_PATH=$(find /kernel/boot -name "initrd.img-*" | head -1) && \ + \ + # Copy kernel and initrd to ESP + echo "Installing kernel..." && \ + mkdir -p /mnt/esp/boot && \ + cp $KERNEL_PATH /mnt/esp/boot/vmlinuz && \ + if [ -f "$INITRD_PATH" ]; then cp $INITRD_PATH /mnt/esp/boot/initrd.img; fi && \ + \ + # Create GRUB configuration + mkdir -p /mnt/esp/boot/grub && \ + cat > /mnt/esp/boot/grub/grub.cfg << 'GRUB_EOF' && \ +set timeout=3 +set default=0 + +menuentry "SparkOS" { + linux /boot/vmlinuz root=LABEL=SparkOS rw init=/sbin/init console=tty1 quiet +} +GRUB_EOF + \ + # Set up root filesystem + echo "Setting up root filesystem..." && \ + mkdir -p /mnt/root/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var/{log,run},root,home/spark,boot} && \ + \ + # Install SparkOS init + cp /build/init /mnt/root/sbin/init && \ + chmod 755 /mnt/root/sbin/init && \ + \ + # Install busybox + echo "Installing busybox..." && \ + cp /bin/busybox /mnt/root/bin/busybox && \ + chmod 755 /mnt/root/bin/busybox && \ + \ + # Create busybox symlinks for essential commands + for cmd in sh ls cat echo mount umount mkdir rm cp mv chmod chown ln ps kill; do \ + ln -sf busybox /mnt/root/bin/$cmd; \ + done && \ + \ + # Create system configuration files + echo "sparkos" > /mnt/root/etc/hostname && \ + echo "127.0.0.1 localhost" > /mnt/root/etc/hosts && \ + echo "127.0.1.1 sparkos" >> /mnt/root/etc/hosts && \ + echo "root:x:0:0:root:/root:/bin/sh" > /mnt/root/etc/passwd && \ + echo "spark:x:1000:1000:SparkOS User:/home/spark:/bin/sh" >> /mnt/root/etc/passwd && \ + echo "root:x:0:" > /mnt/root/etc/group && \ + echo "spark:x:1000:" >> /mnt/root/etc/group && \ + \ + # Create README + cat > /mnt/root/README.txt << 'README_EOF' && \ +SparkOS UEFI-Bootable Image + +This is a UEFI-bootable disk image with: +- GPT partition table +- EFI System Partition (ESP) with FAT32 filesystem +- GRUB UEFI bootloader +- Linux kernel +- SparkOS init system +- Busybox utilities + +The image can be written to a USB drive and booted on UEFI systems: + sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress + sudo sync + +Boot options: +- UEFI boot support (tested on x86_64 systems) +- Automatic boot after 3 seconds +- Console on tty1 + +For more information, see: https://github.com/johndoe6345789/SparkOS +README_EOF + \ + # Sync and unmount + echo "Finalizing image..." && \ + sync && \ + umount /mnt/esp && \ + umount /mnt/root && \ + losetup -d $LOOP_DEV && \ + \ + # Compress the image + echo "Compressing image..." && \ + gzip -9 /output/sparkos.img && \ + echo "UEFI-bootable image created: /output/sparkos.img.gz" + +# Final stage - export the image +FROM scratch AS export +COPY --from=image-builder /output/sparkos.img.gz /sparkos.img.gz diff --git a/Makefile b/Makefile index 46b1622..a0f37dc 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ DESTDIR = rootfs IMAGE = sparkos.img IMAGE_SIZE = 512M -.PHONY: all clean init image help install +.PHONY: all clean init image image-docker help install docker-release all: init @@ -15,13 +15,19 @@ help: @echo "SparkOS Build System" @echo "====================" @echo "Targets:" - @echo " make init - Build the init system" - @echo " make install - Install init to rootfs" - @echo " make image - Create bootable dd-able image (requires root)" - @echo " make clean - Clean build artifacts" + @echo " make init - Build the init system" + @echo " make install - Install init to rootfs" + @echo " make image - Create bootable dd-able image (requires root)" + @echo " make image-docker - Create bootable image using Docker (no root required)" + @echo " make docker-release - Build release package using Docker (no root required)" + @echo " make clean - Clean build artifacts" @echo "" @echo "Note: Creating a bootable image requires root privileges" @echo " and various tools (debootstrap, syslinux, etc.)" + @echo "" + @echo "For easier image building, use Docker:" + @echo " make image-docker" + @echo " OR: ./scripts/build-image.sh" init: src/init.c @echo "Building SparkOS init system..." @@ -42,9 +48,18 @@ image: install fi @./scripts/create_image.sh +image-docker: + @echo "Building bootable image using Docker..." + @./scripts/build-image.sh + +docker-release: + @echo "Building release package using Docker..." + @./scripts/docker-release.sh + clean: @echo "Cleaning build artifacts..." rm -f init rm -f $(IMAGE) rm -rf build/ + rm -rf release/ @echo "Clean complete" diff --git a/README.md b/README.md index 58921a0..00d16b1 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,39 @@ To create bootable images (optional): ## Quick Start -### Using Pre-built Releases (Easiest) +### Using UEFI-Bootable Disk Image (Recommended - Boot from USB) -Download the latest release package from the [GitHub Releases page](https://github.com/johndoe6345789/SparkOS/releases): +Download the UEFI-bootable image from the [GitHub Releases page](https://github.com/johndoe6345789/SparkOS/releases): + +```bash +# Download the disk image (replace VERSION with actual version, e.g., v1.0.0) +wget https://github.com/johndoe6345789/SparkOS/releases/download/VERSION/sparkos.img.gz + +# Decompress the image +gunzip sparkos.img.gz + +# Write to USB drive (Linux - BE CAREFUL!) +sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress oflag=sync +``` + +**⚠️ WARNING**: Replace `/dev/sdX` with your actual USB device (e.g., `/dev/sdb`). This will **DESTROY ALL DATA** on the target drive! + +**Boot Instructions:** +1. Insert the USB drive into a UEFI-capable system +2. Enter BIOS/UEFI settings (usually F2, F12, DEL, or ESC at boot) +3. Select the USB drive as boot device +4. SparkOS will boot automatically + +The UEFI-bootable disk image includes: +- ✅ **UEFI boot support** with GRUB bootloader +- ✅ **GPT partition table** with ESP (EFI System Partition) +- ✅ **Linux kernel** ready to boot +- ✅ **SparkOS init system** and busybox utilities +- ✅ **Ready to boot** - No additional setup required + +### Using Pre-built Binary Package + +Download the binary package from the [GitHub Releases page](https://github.com/johndoe6345789/SparkOS/releases): ```bash # Download the latest release (replace VERSION with actual version, e.g., v1.0.0) @@ -84,6 +114,9 @@ docker run --rm ghcr.io/johndoe6345789/sparkos:latest docker build -t sparkos:local . docker run --rm sparkos:local +# Or use Docker Compose for even simpler testing +docker-compose up + # Build for specific architecture docker buildx build --platform linux/amd64 -t sparkos:amd64 --load . docker buildx build --platform linux/arm64 -t sparkos:arm64 --load . @@ -97,6 +130,18 @@ The Docker image includes: Images are automatically built and published to [GitHub Container Registry](https://github.com/johndoe6345789/SparkOS/pkgs/container/sparkos) on every push to main branch. +**Building Releases with Docker (No Root Required):** + +Create release packages easily using Docker without needing root privileges or special tools: + +```bash +# Build a release package for version v1.0.0 +./scripts/docker-release.sh v1.0.0 + +# The release ZIP will be created in release/sparkos-release.zip +# This is the same artifact that GitHub Actions creates +``` + ### Building the Init System ```bash @@ -117,9 +162,32 @@ make init make install ``` -### Creating a Bootable Image (Advanced) +### Creating a UEFI-Bootable Image -⚠️ **Warning**: Creating bootable images requires root privileges and proper tools. +**Using Docker (Recommended - No Root Required):** + +Build UEFI-bootable disk images easily using Docker without needing root privileges on your host: + +```bash +# Build the UEFI-bootable disk image using Docker +make image-docker + +# Or use the script directly +./scripts/build-image.sh + +# The compressed UEFI-bootable image will be in release/sparkos.img.gz +``` + +This creates a complete UEFI-bootable image with: +- GPT partition table +- EFI System Partition (ESP) with FAT32 +- GRUB UEFI bootloader +- Linux kernel +- SparkOS init system and busybox + +**Traditional Method (Requires Root):** + +⚠️ **Warning**: This method is for creating custom partitioned images and requires root privileges. ```bash # Install required tools (Ubuntu/Debian) @@ -243,16 +311,66 @@ docker buildx build --platform linux/amd64,linux/arm64 -t sparkos:multiarch . # Test the image docker run --rm sparkos:dev +# Or use Docker Compose +docker-compose up + # Inspect the init binary docker run --rm sparkos:dev sh -c "ls -lh /sparkos/rootfs/sbin/init" ``` +### Creating Releases + +**Using Docker (Recommended - No Root Required):** + +Build release packages locally using Docker without needing root privileges: + +```bash +# Build a release package +./scripts/docker-release.sh v1.0.0 + +# The release ZIP will be in release/sparkos-release.zip +# This is identical to what GitHub Actions creates +``` + +**Creating a GitHub Release:** + +1. **Commit and push your changes** to the main branch +2. **Create and push a version tag:** + ```bash + git tag v1.0.0 + git push origin v1.0.0 + ``` +3. **GitHub Actions will automatically:** + - Build the init binary + - Create the release package ZIP + - Build and publish Docker images (AMD64 + ARM64) + - Create a GitHub Release with the artifacts + - Publish to GitHub Container Registry + +The release will be available at: +- **GitHub Releases:** https://github.com/johndoe6345789/SparkOS/releases +- **Docker Images:** `ghcr.io/johndoe6345789/sparkos:v1.0.0` + +**Manual Release Creation:** + +You can also create a release manually: +1. Go to https://github.com/johndoe6345789/SparkOS/releases/new +2. Choose or create a tag (e.g., `v1.0.0`) +3. Fill in the release title and description +4. Upload the `sparkos-release.zip` (built locally with `docker-release.sh`) +5. Publish the release + +For detailed instructions on creating releases, see [RELEASING.md](RELEASING.md). + ### Building Components ```bash # Build init system only make init +# Build release package using Docker +make docker-release + # Install to rootfs make install diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..ed3340a --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,233 @@ +# SparkOS Release Guide + +This guide explains how to create and publish releases for SparkOS. + +## Release Methods + +SparkOS supports two ways to create releases: + +1. **Automatic (Recommended)**: Push a version tag to trigger GitHub Actions +2. **Manual**: Build locally and upload to GitHub + +## Method 1: Automatic Release (Recommended) + +This method uses GitHub Actions to automatically build and publish releases. + +### Prerequisites + +- Push access to the repository +- All changes committed and pushed to `main` branch + +### Steps + +1. **Ensure everything is ready** + ```bash + # Make sure you're on main and up to date + git checkout main + git pull origin main + + # Verify the build works + make init + ``` + +2. **Create and push a version tag** + ```bash + # Create a tag (use semantic versioning: v1.0.0, v2.1.3, etc.) + git tag v1.0.0 + + # Push the tag to GitHub + git push origin v1.0.0 + ``` + +3. **Wait for GitHub Actions** + - Go to https://github.com/johndoe6345789/SparkOS/actions + - Two workflows will run: + - **"Build and Release"**: Creates the release ZIP + - **"Docker Build and Publish"**: Publishes Docker images + - Both should complete successfully (green checkmarks) + +4. **Verify the release** + - Go to https://github.com/johndoe6345789/SparkOS/releases + - Your new release should appear with `sparkos-release.zip` attached + - Docker images are available at `ghcr.io/johndoe6345789/sparkos:v1.0.0` + +### What Gets Published + +When you push a version tag, GitHub Actions automatically: + +- ✅ Builds the init binary (statically linked) +- ✅ Creates a release package ZIP containing: + - Compiled init binary + - Complete source code + - Build scripts and configuration + - Root filesystem structure + - Full documentation +- ✅ Creates a GitHub Release at `/releases` +- ✅ Builds multi-architecture Docker images (AMD64 + ARM64) +- ✅ Publishes Docker images to GitHub Container Registry +- ✅ Tags Docker images with version number + +## Method 2: Manual Release + +Use this method if you need to build releases locally without pushing tags. + +### Prerequisites + +- Docker installed locally +- `zip` utility installed + +### Steps + +1. **Build the release package using Docker** + ```bash + # Build for a specific version + ./scripts/docker-release.sh v1.0.0 + + # Or use Make + make docker-release + + # The package will be created at: release/sparkos-release.zip + ``` + +2. **Verify the package** + ```bash + # Check the file was created + ls -lh release/sparkos-release.zip + + # List contents + unzip -l release/sparkos-release.zip | head -40 + ``` + +3. **Create a GitHub Release manually** + - Go to https://github.com/johndoe6345789/SparkOS/releases/new + - Fill in: + - **Tag**: Create a new tag (e.g., `v1.0.0`) + - **Title**: `SparkOS v1.0.0` (or similar) + - **Description**: List changes and features + - **Upload** the `sparkos-release.zip` file + - Click **"Publish release"** + +### What's Included + +The Docker-based release script creates the exact same package as GitHub Actions: + +- Compiled init binary (statically linked, ready to use) +- Complete source code +- Build scripts (including `docker-release.sh`) +- Configuration files +- Root filesystem structure +- Full documentation (README, ARCHITECTURE, CONTRIBUTING) + +## Testing Before Release + +Always test your changes before creating a release: + +### Test the Build + +```bash +# Test compilation +make clean && make init + +# Verify the binary +file init +ldd init # Should show "not a dynamic executable" (static) +``` + +### Test with Docker + +```bash +# Build the Docker image +docker build -t sparkos:test . + +# Run the test environment +docker run --rm sparkos:test + +# Or use Docker Compose +docker-compose up +``` + +### Test the Release Script + +```bash +# Build a test release package +./scripts/docker-release.sh test + +# Verify it was created +ls -lh release/sparkos-release.zip +``` + +## Release Checklist + +Before creating a release: + +- [ ] All changes are committed and pushed to `main` +- [ ] Build succeeds: `make init` works +- [ ] Docker build succeeds: `docker build -t sparkos:test .` works +- [ ] Documentation is up to date (README, ARCHITECTURE, etc.) +- [ ] Version follows semantic versioning (vMAJOR.MINOR.PATCH) +- [ ] CHANGELOG or release notes are prepared (if applicable) + +## Troubleshooting + +### GitHub Actions Fails + +1. Check the workflow logs at https://github.com/johndoe6345789/SparkOS/actions +2. Common issues: + - Build errors: Check if `make init` works locally + - Permission errors: Ensure `contents: write` permission in workflow + - Docker errors: Verify Dockerfile builds locally + +### Release Not Created + +- Ensure you pushed a tag starting with `v` (e.g., `v1.0.0`) +- Check that the workflow has `contents: write` permissions +- Verify the workflow completed successfully (green checkmark) + +### Docker Images Not Published + +- Check the "Docker Build and Publish" workflow logs +- Ensure the tag was pushed (not just created locally) +- Verify `packages: write` permission in workflow + +## Version Numbering + +Use semantic versioning for releases: + +- **v1.0.0**: First stable release +- **v1.1.0**: New features, backward compatible +- **v1.1.1**: Bug fixes only +- **v2.0.0**: Breaking changes + +## Release Artifacts + +Each release includes: + +1. **GitHub Release Page** + - Release notes + - Downloadable `sparkos-release.zip` + - Link to Docker images + +2. **Docker Images** (automatically published) + - `ghcr.io/johndoe6345789/sparkos:v1.0.0` (specific version) + - `ghcr.io/johndoe6345789/sparkos:1.0` (minor version) + - `ghcr.io/johndoe6345789/sparkos:1` (major version) + - `ghcr.io/johndoe6345789/sparkos:latest` (if released from main) + +3. **Multi-Architecture Support** + - Linux AMD64 (x86_64) + - Linux ARM64 (aarch64) + +## Next Steps + +After releasing: + +1. Announce the release (if applicable) +2. Update documentation links to point to the new version +3. Test the release on different platforms +4. Monitor for issues and prepare patches if needed + +## Support + +For questions or issues with releases: +- Open an issue: https://github.com/johndoe6345789/SparkOS/issues +- Check workflow logs: https://github.com/johndoe6345789/SparkOS/actions diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..26c688a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3.8' + +services: + sparkos: + build: + context: . + dockerfile: Dockerfile + image: sparkos:local + container_name: sparkos-test + # The container will run the test script and exit + # Use 'docker-compose up' to test the build diff --git a/scripts/build-image.sh b/scripts/build-image.sh new file mode 100755 index 0000000..34749d2 --- /dev/null +++ b/scripts/build-image.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# Build SparkOS disk image using Docker +# This script builds a .img file without requiring root on the host + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +OUTPUT_DIR="$PROJECT_ROOT/release" + +echo "SparkOS Disk Image Builder (Docker)" +echo "====================================" +echo "" + +# Clean previous build +if [ -f "$OUTPUT_DIR/sparkos.img.gz" ]; then + echo "Cleaning previous image..." + rm -f "$OUTPUT_DIR/sparkos.img.gz" +fi + +mkdir -p "$OUTPUT_DIR" + +echo "Building disk image using Docker..." +echo "This may take a few minutes..." +echo "" + +# Build the image builder container +cd "$PROJECT_ROOT" +docker buildx build \ + --file Dockerfile.image \ + --target image-builder \ + --tag sparkos:image-builder \ + --load \ + . + +# Extract the image +echo "" +echo "Extracting disk image..." +CONTAINER_ID=$(docker create sparkos:image-builder) +docker cp "$CONTAINER_ID:/output/sparkos.img.gz" "$OUTPUT_DIR/sparkos.img.gz" +docker rm "$CONTAINER_ID" > /dev/null + +echo "" +echo "SUCCESS! Disk image created:" +echo " Location: $OUTPUT_DIR/sparkos.img.gz" +echo " Size: $(du -h "$OUTPUT_DIR/sparkos.img.gz" | cut -f1)" +echo "" + +# Show decompressed size +echo "Decompressed size:" +DECOMPRESSED_SIZE=$(gunzip -l "$OUTPUT_DIR/sparkos.img.gz" | tail -1 | awk '{print $2}') +echo " $(numfmt --to=iec-i --suffix=B $DECOMPRESSED_SIZE)" +echo "" + +echo "To write to a USB drive:" +echo " gunzip $OUTPUT_DIR/sparkos.img.gz" +echo " sudo dd if=$OUTPUT_DIR/sparkos.img of=/dev/sdX bs=4M status=progress" +echo "" +echo "WARNING: Replace /dev/sdX with your actual USB device!" +echo " This will DESTROY all data on the target drive!" +echo "" + +echo "To inspect the image:" +echo " gunzip -c $OUTPUT_DIR/sparkos.img.gz > /tmp/sparkos.img" +echo " sudo mount -o loop /tmp/sparkos.img /mnt" +echo " ls -la /mnt" +echo " sudo umount /mnt" diff --git a/scripts/docker-release.sh b/scripts/docker-release.sh new file mode 100755 index 0000000..60359db --- /dev/null +++ b/scripts/docker-release.sh @@ -0,0 +1,183 @@ +#!/bin/bash +# SparkOS Docker-based Release Builder +# Build release artifacts using Docker (no root required) + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +RELEASE_DIR="$PROJECT_ROOT/release" +VERSION="${1:-dev}" + +echo "SparkOS Docker Release Builder" +echo "==============================" +echo "" +echo "Version: $VERSION" +echo "" + +# Clean previous release +if [ -d "$RELEASE_DIR" ]; then + echo "Cleaning previous release..." + rm -rf "$RELEASE_DIR" +fi + +mkdir -p "$RELEASE_DIR" + +# Build using Docker (multi-stage build) +echo "Building init binary using Docker..." +docker build -t sparkos:build-temp --target builder "$PROJECT_ROOT" + +# Extract the built binary +echo "Extracting init binary..." +CONTAINER_ID=$(docker create sparkos:build-temp) +docker cp "$CONTAINER_ID:/build/init" "$RELEASE_DIR/init" +docker rm "$CONTAINER_ID" > /dev/null + +# Verify the binary +echo "" +echo "Verifying init binary..." +if [ ! -f "$RELEASE_DIR/init" ]; then + echo "ERROR: Failed to extract init binary" + exit 1 +fi + +ls -lh "$RELEASE_DIR/init" +file "$RELEASE_DIR/init" + +# Create release package structure +echo "" +echo "Creating release package..." +mkdir -p "$RELEASE_DIR/sparkos" + +# Copy compiled binary +cp "$RELEASE_DIR/init" "$RELEASE_DIR/sparkos/" + +# Copy essential files +cp "$PROJECT_ROOT/README.md" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/LICENSE" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/ARCHITECTURE.md" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/CONTRIBUTING.md" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/Makefile" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/Dockerfile" "$RELEASE_DIR/sparkos/" + +# Copy source for reference +cp -r "$PROJECT_ROOT/src" "$RELEASE_DIR/sparkos/" + +# Copy scripts +cp -r "$PROJECT_ROOT/scripts" "$RELEASE_DIR/sparkos/" + +# Copy config +cp -r "$PROJECT_ROOT/config" "$RELEASE_DIR/sparkos/" + +# Copy rootfs structure (without generated content) +mkdir -p "$RELEASE_DIR/sparkos/rootfs" +for dir in etc root home; do + if [ -d "$PROJECT_ROOT/rootfs/$dir" ]; then + cp -r "$PROJECT_ROOT/rootfs/$dir" "$RELEASE_DIR/sparkos/rootfs/" + fi +done + +# Create README for the release +cat > "$RELEASE_DIR/sparkos/RELEASE_README.md" << 'EOF' +# SparkOS Release Package + +This package contains the compiled SparkOS init system and all necessary files to run or build SparkOS. + +## Contents + +- `init` - The compiled init binary (statically linked) +- `src/` - Source code for the init system +- `scripts/` - Build and setup scripts +- `config/` - Configuration files +- `rootfs/` - Root filesystem structure +- `Dockerfile` - Docker image definition +- `Makefile` - Build system +- Documentation files (README.md, ARCHITECTURE.md, etc.) + +## Quick Start + +### Using the Pre-built Binary + +The `init` binary is already compiled and ready to use: + +```bash +# Copy to your rootfs +cp init /path/to/your/rootfs/sbin/init +chmod 755 /path/to/your/rootfs/sbin/init +``` + +### Using Docker + +The easiest way to test SparkOS: + +```bash +# Build the Docker image +docker build -t sparkos . + +# Run the test environment +docker run --rm sparkos +``` + +### Rebuilding from Source + +If you need to rebuild: + +```bash +# Build the init system +make init + +# Install to rootfs +make install +``` + +## Using Docker for Releases + +Build release artifacts without needing root or special tools: + +```bash +# Build release package +./scripts/docker-release.sh v1.0.0 + +# The release package will be in release/sparkos-release.zip +``` + +## System Requirements + +- Linux system with kernel 3.x or later +- Busybox for shell and utilities +- For building: Docker or GCC compiler and Make + +## Documentation + +See README.md for complete documentation, including: +- Building instructions +- Docker usage +- Network configuration +- Development guidelines + +## Support + +For issues and questions, visit: https://github.com/johndoe6345789/SparkOS +EOF + +# Create release archive +echo "" +echo "Creating release archive..." +cd "$RELEASE_DIR" +zip -q -r "sparkos-release.zip" sparkos/ +cd "$PROJECT_ROOT" + +echo "" +echo "SUCCESS! Release package created:" +echo " Location: $RELEASE_DIR/sparkos-release.zip" +echo " Size: $(du -h "$RELEASE_DIR/sparkos-release.zip" | cut -f1)" +echo "" +echo "Contents:" +ls -lh "$RELEASE_DIR/sparkos-release.zip" +echo "" +echo "To verify the contents:" +echo " unzip -l $RELEASE_DIR/sparkos-release.zip | head -40" +echo "" +echo "To test the Docker image:" +echo " docker build -t sparkos:$VERSION ." +echo " docker run --rm sparkos:$VERSION"