Build Utile OS 26 ISO #23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Codeswallop Linux 26.04 ISO | |
| on: | |
| push: | |
| branches: [ "main" ] | |
| workflow_dispatch: | |
| jobs: | |
| build-custom-iso: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v6.0.2 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6.4.0 | |
| with: | |
| node-version: '24' | |
| cache: 'pnpm' | |
| - name: Install OS Dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y squashfs-tools xorriso mtools fdisk rsync | |
| - name: Compile Bash Script (Node.js) | |
| env: | |
| # If this was triggered by a tag (release), set to release. Otherwise, nightly will automatically be used. | |
| BUILD_TYPE: ${{ startsWith(github.ref, 'refs/tags/') && 'release' || '' }} | |
| run: | | |
| # Install any dependencies your node script needs | |
| pnpm install | |
| # Run the compilation script | |
| SH_NAME=$(node . | tail -n 1) | |
| TEMP_NAME=${SH_NAME#codeswallop-linux-} | |
| TAG_NAME=${TEMP_NAME%.sh} | |
| ISO_NAME="${SH_NAME%.sh}.iso" | |
| echo "SH_NAME=$SH_NAME" >> $GITHUB_ENV | |
| echo "ISO_NAME=$ISO_NAME" >> $GITHUB_ENV | |
| echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV | |
| # Verify the output exists | |
| ls -la "dist/$SH_NAME" | |
| - name: Download Base Ubuntu 26.04 ISO | |
| run: | | |
| wget -q -O base-ubuntu-26.04.iso https://releases.ubuntu.com/resolute/ubuntu-26.04-desktop-amd64.iso | |
| - name: Extract Base ISO | |
| run: | | |
| mkdir -p source-iso custom-iso squashfs-root | |
| # Mount the original ISO and copy its contents | |
| sudo mount -o loop base-ubuntu-26.04.iso source-iso | |
| rsync -a source-iso/ custom-iso/ | |
| sudo umount source-iso | |
| - name: Extract Root Filesystem (SquashFS) | |
| run: | | |
| # Find the largest .squashfs file in the casper directory dynamically | |
| SQUASHFS_FILE=$(ls -S custom-iso/casper/*.squashfs | head -n 1) | |
| echo "Found root filesystem: $SQUASHFS_FILE" | |
| # Save the exact path to GitHub ENV so the re-compress step knows what to overwrite | |
| echo "SQUASHFS_PATH=$SQUASHFS_FILE" >> $GITHUB_ENV | |
| # Extract it | |
| sudo unsquashfs -f -d squashfs-root "$SQUASHFS_FILE" | |
| - name: Find available squashed filesystems | |
| run: | | |
| ls -la custom-iso/casper/ | |
| - name: Inspect and Prune Extended Installation | |
| run: | | |
| echo "--- ORIGINAL install-sources.yaml ---" | |
| cat custom-iso/casper/install-sources.yaml | |
| echo "--- END OF ORIGINAL install-sources.yaml ---" | |
| # 1. Delete the standard squashfs and its associated manifests | |
| echo "Pruning minimal.standard files to save ~600MB..." | |
| sudo rm -fv custom-iso/casper/minimal.standard.* | |
| # 2. Remove the "Extended Installation" profile from the installer UI | |
| # yq is pre-installed on ubuntu-latest runners | |
| sudo yq eval 'del(.sources[] | select(.id == "ubuntu-desktop"))' -i custom-iso/casper/install-sources.yaml | |
| echo "--- UPDATED install-sources.yaml ---" | |
| cat custom-iso/casper/install-sources.yaml | |
| - name: Setup Chroot Environment & Run Custom Script | |
| run: | | |
| # Copy script | |
| sudo cp dist/${{ env.SH_NAME }} squashfs-root/opt/ | |
| sudo chmod +x squashfs-root/opt/${{ env.SH_NAME }} | |
| # Bind mounts | |
| sudo mount --bind /dev squashfs-root/dev | |
| sudo mount --bind /run squashfs-root/run | |
| sudo mount -t proc /proc squashfs-root/proc | |
| sudo mount -t sysfs /sys squashfs-root/sys | |
| # Fix for the "same file" error: Remove and copy with dereference | |
| sudo rm -f squashfs-root/etc/resolv.conf | |
| sudo cp -L /etc/resolv.conf squashfs-root/etc/resolv.conf | |
| # Execute in chroot | |
| sudo chroot squashfs-root /bin/bash -xlc "/opt/${{ env.SH_NAME }}" | |
| # Proper Cleanup (Unmount in reverse order) | |
| sudo umount squashfs-root/sys | |
| sudo umount squashfs-root/proc | |
| sudo umount squashfs-root/run | |
| sudo umount squashfs-root/dev | |
| - name: Apply Custom Disk Metadata | |
| run: | | |
| # Matches: Disk Name and Release | |
| echo 'Codeswallop 26.04 amd64 "Resolute Raccoon" - Release amd64 (20260430)' | sudo tee custom-iso/.disk/info | |
| # Matches: Release URL | |
| echo 'https://github.com/Proman4713/Codeswallop-Linux/releases/tag/${{ env.TAG_NAME }}' | sudo tee custom-iso/.disk/release_notes_url | |
| # Set the Version marker | |
| echo '2026.04.30' | sudo tee custom-iso/.disk/codeswallop_version | |
| - name: Re-compress Root Filesystem | |
| run: | | |
| # Remove the old base filesystem using the path we found earlier | |
| sudo rm "${{ env.SQUASHFS_PATH }}" | |
| # Compress the modified filesystem back to the exact same file name | |
| sudo mksquashfs squashfs-root "${{ env.SQUASHFS_PATH }}" -comp zstd -b 1M -noappend | |
| # Update the matching filesystem size manifest (.size file) | |
| SIZE_FILE="${{ env.SQUASHFS_PATH }}" | |
| SIZE_FILE="${SIZE_FILE%.squashfs}.size" | |
| printf $(sudo du -sx --block-size=1 squashfs-root | cut -f1) | sudo tee "$SIZE_FILE" | |
| - name: Update MD5 Checksums | |
| run: | | |
| cd custom-iso | |
| sudo rm -f md5sum.txt | |
| # Calculate new checksums for integrity verification during boot | |
| sudo find -type f -print0 | sudo xargs -0 md5sum | grep -v isolinux/boot.cat | sudo tee md5sum.txt | |
| cd .. | |
| - name: Generate Final ISO | |
| run: | | |
| # 1. Extract the Master Boot Record (MBR) | |
| dd if=base-ubuntu-26.04.iso bs=512 count=1 of=isohdpfx.bin | |
| # 2. Extract the hidden EFI partition | |
| # Using a more robust selector for the EFI partition type | |
| EFI_START=$(sfdisk -J base-ubuntu-26.04.iso | jq -r '.partitiontable.partitions[] | select(.type=="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" or .type=="ef") | .start' | head -n 1) | |
| EFI_SIZE=$(sfdisk -J base-ubuntu-26.04.iso | jq -r '.partitiontable.partitions[] | select(.type=="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" or .type=="ef") | .size' | head -n 1) | |
| dd if=base-ubuntu-26.04.iso bs=512 skip=$EFI_START count=$EFI_SIZE of=efi.img | |
| mkdir -p custom-iso/boot/grub | |
| sudo mv efi.img custom-iso/boot/grub/efi.img | |
| # 3. Generate the ISO with Large File Support (-iso-level 3) | |
| sudo xorriso -as mkisofs \ | |
| -iso-level 3 \ | |
| -full-iso9660-filenames \ | |
| -volid "Codeswallop 26.04 amd64" \ | |
| -eltorito-boot boot/grub/i386-pc/eltorito.img \ | |
| -no-emul-boot -boot-load-size 4 -boot-info-table \ | |
| --eltorito-catalog boot.catalog \ | |
| -eltorito-alt-boot \ | |
| -e boot/grub/efi.img \ | |
| -no-emul-boot \ | |
| -isohybrid-mbr isohdpfx.bin \ | |
| -isohybrid-gpt-basdat \ | |
| -append_partition 2 0xef custom-iso/boot/grub/efi.img \ | |
| -output ${{ env.ISO_NAME }} \ | |
| -graft-points \ | |
| "/=$(pwd)/custom-iso" | |
| - name: Upload Custom ISO to GitHub Releases | |
| uses: softprops/action-gh-release@v3.0.0 | |
| with: | |
| tag_name: ${{ env.TAG_NAME }} | |
| name: Codeswallop Linux ${{ env.TAG_NAME }} | |
| files: | | |
| ${{ env.ISO_NAME }} | |
| dist/${{ env.SH_NAME }} | |
| # Automatically mark as pre-release if it's a nightly or beta | |
| prerelease: ${{ contains(env.TAG_NAME, 'nightly') || contains(env.TAG_NAME, 'beta') }} |