--- name: Reusable Build "on": workflow_call: inputs: image_version: description: 'Which image version to build' type: string required: true image_tag: description: 'Image tag to build from' type: string default: lts upload_artifacts: description: 'Upload ISOs as job artifacts' type: boolean default: false upload_r2: description: 'Upload ISOs to Cloudflare R2' type: boolean default: true secrets: R2_ACCESS_KEY_ID_2025: required: false R2_SECRET_ACCESS_KEY_2025: required: false R2_ENDPOINT_2025: required: false pull_request: paths: - ".github/workflows/reusable-build-iso-anaconda.yml" - "iso/enable_anaconda.sh" env: IMAGE_REGISTRY: "ghcr.io/ublue-os" IMAGE_NAME: "bluefin" jobs: # Define which variants to build based on input # This job ensures strict separation: each image_version builds ONLY its own ISOs determine-matrix: name: Determine Build Matrix runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Set Matrix id: set-matrix run: | # Define the matrix based on the input selection # When called via workflow_call, the image_version input determines which ISOs to build # Each variant builds ONLY its own specific ISOs - no cross-contamination # NOTE: Check if inputs.image_version is provided (indicates workflow_call) # rather than checking github.event_name, which may not be reliable if [[ -n "${{ inputs.image_version }}" ]]; then case "${{ inputs.image_version }}" in "lts") # LTS variant: builds ONLY LTS ISOs (4 total) matrix='{"include":[ {"platform":"amd64","flavor":"main","image_version":"lts"}, {"platform":"arm64","flavor":"main","image_version":"lts"}, {"platform":"amd64","flavor":"gdx","image_version":"lts"}, {"platform":"arm64","flavor":"gdx","image_version":"lts"} ]}' ;; "lts-hwe") # LTS-HWE variant: builds ONLY LTS-HWE ISOs (2 total) matrix='{"include":[ {"platform":"amd64","flavor":"main","image_version":"lts-hwe"}, {"platform":"arm64","flavor":"main","image_version":"lts-hwe"} ]}' ;; "stable") # Stable variant: builds ONLY Stable ISOs (2 total) matrix='{"include":[ {"platform":"amd64","flavor":"main","image_version":"stable"}, {"platform":"amd64","flavor":"nvidia-open","image_version":"stable"} ]}' ;; "all") # "all" is reserved for testing - builds all variants matrix='{"include":[ {"platform":"amd64","flavor":"main","image_version":"stable"}, {"platform":"amd64","flavor":"nvidia-open","image_version":"stable"}, {"platform":"amd64","flavor":"main","image_version":"lts"}, {"platform":"arm64","flavor":"main","image_version":"lts"}, {"platform":"amd64","flavor":"gdx","image_version":"lts"}, {"platform":"arm64","flavor":"gdx","image_version":"lts"}, {"platform":"amd64","flavor":"main","image_version":"lts-hwe"}, {"platform":"arm64","flavor":"main","image_version":"lts-hwe"} ]}' ;; esac else # Default for when image_version is not provided (e.g., pull_request triggers) # Build Stable only for faster PR validation matrix='{"include":[ {"platform":"amd64","flavor":"main","image_version":"stable"}, {"platform":"amd64","flavor":"nvidia-open","image_version":"stable"} ]}' fi # Compact the JSON and output it compact_matrix=$(echo "$matrix" | jq -c .) echo "matrix=$compact_matrix" >> $GITHUB_OUTPUT echo "Generated matrix:" echo "$matrix" | jq . build: name: Build ISOs runs-on: ${{ matrix.platform == 'amd64' && 'ubuntu-24.04' || 'ubuntu-24.04-arm' }} needs: determine-matrix strategy: fail-fast: false matrix: ${{ fromJSON(needs.determine-matrix.outputs.matrix) }} permissions: contents: read packages: read id-token: write steps: - name: Install dependencies if: matrix.platform == 'arm64' run: | set -x sudo apt update -y sudo apt install -y \ podman - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: submodules: recursive - name: Setup Just uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 - name: Check Just Syntax shell: bash run: | just check - name: Format image ref id: image_ref env: FLAVOR: ${{ matrix.flavor }} run: | set -eoux pipefail # Standard variants use the Just recipe to determine image name image_name=$(just image_name "bluefin" "${{ matrix.image_version}}" "${{ matrix.flavor}}") image_ref="${IMAGE_REGISTRY}/${image_name}" artifact_format="$image_name-${{ matrix.image_version }}-$(uname -m)" KARGS="NONE" echo "image_ref=$image_ref" >> "${GITHUB_OUTPUT}" echo "artifact_format=$artifact_format" >> "${GITHUB_OUTPUT}" echo "kargs=$KARGS" >> "${GITHUB_OUTPUT}" - name: Generate titanoboa-compatible file list id: flatpak_list run: | set -eoux pipefail FILE_LIST="$(mktemp)" git clone https://github.com/projectbluefin/common.git common find common -iname "*system-flatpaks.Brewfile" -exec cat '{}' ';' | grep -v '#' | grep -F -e "flatpak" | sed 's/flatpak //' | tr -d '"' | tee "${FILE_LIST}" echo "file_list_path=${FILE_LIST}" | tee "${GITHUB_OUTPUT}" - name: Build ISO id: build uses: ublue-os/titanoboa@main with: image-ref: ${{ steps.image_ref.outputs.image_ref }}:${{ inputs.image_tag || matrix.image_version }} flatpaks-list: ${{ steps.flatpak_list.outputs.file_list_path }} hook-post-rootfs: ${{ (matrix.image_version == 'lts' || matrix.image_version == 'lts-hwe') && format('{0}/iso_files/configure_lts_iso_anaconda.sh', github.workspace) || format('{0}/iso_files/configure_iso_anaconda.sh', github.workspace) }} kargs: ${{ steps.image_ref.outputs.kargs }} builder-distro: ${{ (matrix.image_version == 'lts' || matrix.image_version == 'lts-hwe') && 'centos' || 'fedora' }} - name: Rename ISO id: rename env: OUTPUT_PATH: ${{ steps.build.outputs.iso-dest }} FLAVOR: ${{ matrix.flavor }} OUTPUT_NAME: ${{ steps.image_ref.outputs.artifact_format }} IMAGE_VERSION: ${{ matrix.image_version }} run: | set -x mkdir -p output OUTPUT_DIRECTORY="$(realpath output)" mv "${OUTPUT_PATH}" "${OUTPUT_DIRECTORY}/${OUTPUT_NAME}.iso" (cd "${OUTPUT_DIRECTORY}" && sha256sum "${OUTPUT_NAME}.iso" | tee "${OUTPUT_NAME}.iso-CHECKSUM") echo "output_directory=$OUTPUT_DIRECTORY" >> "${GITHUB_OUTPUT}" - name: Generate Torrent File env: OUTPUT_DIRECTORY: ${{ steps.rename.outputs.output_directory }} OUTPUT_NAME: ${{ steps.image_ref.outputs.artifact_format }} IMAGE_VERSION: ${{ matrix.image_version }} FLAVOR: ${{ matrix.flavor }} PLATFORM: ${{ matrix.platform }} run: | set -eoux pipefail sudo apt-get install -y mktorrent BUILD_DATE=$(date -u +%Y-%m-%d) cd "${OUTPUT_DIRECTORY}" mktorrent \ -a udp://tracker.opentrackr.org:1337/announce \ -a udp://open.tracker.cl:1337/announce \ -a udp://open.demonii.com:1337/announce \ -a udp://tracker.openbittorrent.com:6969/announce \ -a udp://exodus.desync.com:6969/announce \ -a udp://tracker.torrent.eu.org:451/announce \ -a udp://tracker.moeking.me:6969/announce \ -a https://tracker.gbitt.info:443/announce \ -a https://tracker.tamersunion.org:443/announce \ -a wss://tracker.btorrent.xyz \ -a wss://tracker.openwebtorrent.com \ -w "https://projectbluefin.dev/${OUTPUT_NAME}.iso" \ -w "https://download.projectbluefin.io/${OUTPUT_NAME}.iso" \ -c "Bluefin ${IMAGE_VERSION} ${FLAVOR} (${PLATFORM}) - ${BUILD_DATE}" \ -o "${OUTPUT_NAME}.iso.torrent" \ "${OUTPUT_NAME}.iso" sha256sum "${OUTPUT_NAME}.iso.torrent" | tee "${OUTPUT_NAME}.iso.torrent-CHECKSUM" - name: Upload to Job Artifacts if: inputs.upload_artifacts uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: ${{ steps.image_ref.outputs.artifact_format }} if-no-files-found: error path: ${{ steps.rename.outputs.output_directory }} - name: Set up Homebrew if: inputs.upload_r2 && github.event_name != 'pull_request' uses: Homebrew/actions/setup-homebrew@master - name: Install rclone if: inputs.upload_r2 && github.event_name != 'pull_request' run: brew install rclone - name: Upload to CloudFlare if: inputs.upload_r2 && github.event_name != 'pull_request' shell: bash env: RCLONE_CONFIG_R2_TYPE: s3 RCLONE_CONFIG_R2_PROVIDER: Cloudflare RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID_2025 }} RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY_2025 }} RCLONE_CONFIG_R2_REGION: auto RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT_2025 }} SOURCE_DIR: ${{ steps.rename.outputs.output_directory }} run: | rclone copy --log-level INFO --checksum "${SOURCE_DIR}" R2:testing create-prerelease: name: Create GitHub Prerelease needs: build if: github.event_name != 'pull_request' runs-on: ubuntu-latest permissions: contents: write env: RCLONE_CONFIG_R2_TYPE: s3 RCLONE_CONFIG_R2_PROVIDER: Cloudflare RCLONE_CONFIG_R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID_2025 }} RCLONE_CONFIG_R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY_2025 }} RCLONE_CONFIG_R2_REGION: auto RCLONE_CONFIG_R2_ENDPOINT: ${{ secrets.R2_ENDPOINT_2025 }} steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@master - name: Install rclone run: brew install rclone - name: Download Torrent Files from R2 run: | set -eoux pipefail mkdir -p torrents rclone copy R2:testing torrents \ --include "*.iso.torrent" \ --include "*.iso.torrent-CHECKSUM" \ --log-level INFO echo "Downloaded torrent files:" ls -lh torrents/ if [ -z "$(ls torrents/*.torrent 2>/dev/null)" ]; then echo "No torrent files found in R2 testing bucket" exit 1 fi - name: Determine Release Version id: version env: GH_TOKEN: ${{ github.token }} run: | set -eoux pipefail YEAR_MONTH=$(date -u +%y.%m) if gh release view "${YEAR_MONTH}" >/dev/null 2>&1; then PATCH=1 while gh release view "${YEAR_MONTH}.${PATCH}" >/dev/null 2>&1; do PATCH=$((PATCH + 1)) done VERSION="${YEAR_MONTH}.${PATCH}" else VERSION="${YEAR_MONTH}" fi echo "version=${VERSION}" >> $GITHUB_OUTPUT - name: Create GitHub Prerelease env: GH_TOKEN: ${{ github.token }} VERSION: ${{ steps.version.outputs.version }} run: | set -eoux pipefail TORRENT_LIST="" for f in torrents/*.torrent torrents/*.torrent-CHECKSUM; do [ -f "$f" ] && TORRENT_LIST="${TORRENT_LIST} ${f}" done gh release create "${VERSION}" \ --prerelease \ --title "Bluefin ISOs ${VERSION} (Prerelease)" \ --notes "$(cat <<'EOF' ## ⚠️ Prerelease - Testing Builds These ISOs are automatically generated from successful builds and uploaded to the **testing** bucket. **Status**: Not yet promoted to production **For**: Testing and validation only Once validated, these builds can be promoted to production using the promotion workflow. ### How to Download 1. Download a `.torrent` file below 2. Open with a BitTorrent client (qBittorrent, Transmission, etc.) 3. Torrents include web seeds -- downloads work immediately ### Direct Downloads ISOs are also available at https://docs.projectbluefin.io/downloads-testing ### Verify ```bash sha256sum -c .torrent-CHECKSUM ``` EOF )" \ ${TORRENT_LIST}