Publish GardenLinux Images #60
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: Publish GardenLinux Images | |
| on: | |
| schedule: | |
| - cron: '0 0 * * 0' | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'GardenLinux version (leave empty for latest release)' | |
| required: false | |
| type: string | |
| jobs: | |
| resolve-version: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.resolve.outputs.version }} | |
| should_publish: ${{ steps.resolve.outputs.should_publish }} | |
| steps: | |
| - name: Resolve version | |
| id: resolve | |
| run: | | |
| if [ -n "${{ inputs.version }}" ]; then | |
| VERSION="${{ inputs.version }}" | |
| echo "Using manually specified version: $VERSION" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| VERSION=$(curl -s https://api.github.com/repos/gardenlinux/gardenlinux/releases/latest | jq -r '.tag_name') | |
| if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then | |
| echo "::error::Failed to resolve latest GardenLinux release" | |
| exit 1 | |
| fi | |
| echo "Resolved latest GardenLinux release: $VERSION" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| # Only check for duplicates on scheduled runs, manual dispatch always publishes. | |
| if [ "${{ github.event_name }}" = "schedule" ]; then | |
| if docker manifest inspect "ghcr.io/${{ github.repository_owner }}/gardenlinux/gardener:${VERSION}-kvm-amd64" > /dev/null 2>&1; then | |
| echo "Version $VERSION already published, skipping" | |
| echo "should_publish=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| fi | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| publish: | |
| needs: resolve-version | |
| if: needs.resolve-version.outputs.should_publish == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - variant: metal | |
| image_name: gardenlinux | |
| asset_pattern: baremetal_pxe | |
| - variant: kvm | |
| image_name: gardenlinux/gardener | |
| asset_pattern: kvm-gardener_prod | |
| - variant: metal | |
| image_name: gardenlinux/gardener | |
| asset_pattern: baremetal-gardener_pxe | |
| - variant: metal | |
| image_name: gardenlinux/capi | |
| asset_pattern: baremetal-capi | |
| env: | |
| VERSION: ${{ needs.resolve-version.outputs.version }} | |
| IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Fetch artifacts from GitHub release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| ASSETS=$(curl -sH "Authorization: token ${GH_TOKEN}" \ | |
| "https://api.github.com/repos/gardenlinux/gardenlinux/releases/tags/$VERSION" \ | |
| | jq -r '.assets[].name') | |
| for ARCH in amd64 arm64; do | |
| ASSET=$(echo "$ASSETS" | grep -E "^${{ matrix.asset_pattern }}-${ARCH}-.*\.tar\.xz$" | head -1) | |
| if [ -z "$ASSET" ]; then | |
| echo "::error::Could not find asset matching ${{ matrix.asset_pattern }}-${ARCH}-*.tar.xz" | |
| exit 1 | |
| fi | |
| # The asset base name (without .tar.xz) is the prefix for all | |
| # files inside the archive. | |
| ASSET_BASE="${ASSET%.tar.xz}" | |
| echo "Downloading $ASSET" | |
| mkdir -p "artifacts/${ARCH}" | |
| curl -sL "https://github.com/gardenlinux/gardenlinux/releases/download/${VERSION}/${ASSET}" \ | |
| -o "artifacts/${ARCH}/${ASSET}" | |
| echo "Extracting $ASSET" | |
| tar -xf "artifacts/${ARCH}/${ASSET}" -C "artifacts/${ARCH}" | |
| rm "artifacts/${ARCH}/${ASSET}" | |
| if [ "${{ matrix.variant }}" = "kvm" ]; then | |
| RAW_FILE="artifacts/${ARCH}/${ASSET_BASE}.raw" | |
| if [ ! -f "$RAW_FILE" ]; then | |
| echo "::error::Expected .raw file not found at ${RAW_FILE}" | |
| exit 1 | |
| fi | |
| echo "KVM_${ARCH^^}_RAW=${RAW_FILE}" >> "$GITHUB_ENV" | |
| else | |
| # Metal: extract the nested PXE tarball to get initrd, vmlinuz, root.squashfs. | |
| PXE_TARBALL="artifacts/${ARCH}/${ASSET_BASE}.pxe.tar.gz" | |
| if [ ! -f "$PXE_TARBALL" ]; then | |
| echo "::error::Could not find nested PXE tarball at ${PXE_TARBALL}" | |
| exit 1 | |
| fi | |
| tar -xzf "$PXE_TARBALL" -C "artifacts/${ARCH}" | |
| for BIN in initrd vmlinuz root.squashfs; do | |
| if [ ! -f "artifacts/${ARCH}/${BIN}" ]; then | |
| echo "::error::Could not find ${BIN} for ${ARCH}" | |
| exit 1 | |
| fi | |
| done | |
| echo "METAL_${ARCH^^}_DIR=artifacts/${ARCH}" >> "$GITHUB_ENV" | |
| echo "METAL_${ARCH^^}_ASSET_BASE=${ASSET_BASE}" >> "$GITHUB_ENV" | |
| fi | |
| done | |
| - name: Create cmdline files (KVM) | |
| if: matrix.variant == 'kvm' | |
| run: | | |
| echo -n "gl.url=/dev/disk/by-id/virtio-machineboot gl.live=1 gl.ovl=/:tmpfs cgroup_enable=memory swapaccount=1 ignition.firstboot=1 ignition.platform.id=qemu consoleblank=0 console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200" > amd64.cmdline | |
| echo -n "gl.url=/dev/disk/by-id/virtio-machineboot gl.live=1 gl.ovl=/:tmpfs cgroup_enable=memory swapaccount=1 ignition.firstboot=1 ignition.platform.id=qemu consoleblank=0 console=tty0 console=ttyAMA0,115200 earlyprintk=ttyAMA0,115200" > arm64.cmdline | |
| - name: Install systemd-ukify (Metal) | |
| if: matrix.variant == 'metal' | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y systemd-ukify | |
| - name: Build UKI (Metal) | |
| if: matrix.variant == 'metal' | |
| run: | | |
| set -euo pipefail | |
| for ARCH in amd64 arm64; do | |
| ARCH_UPPER="${ARCH^^}" | |
| DIR_VAR="METAL_${ARCH_UPPER}_DIR" | |
| DIR="${!DIR_VAR}" | |
| BASE_VAR="METAL_${ARCH_UPPER}_ASSET_BASE" | |
| ASSET_BASE="${!BASE_VAR}" | |
| # Extract the arch-correct EFI stub from the rootfs tar. | |
| if [ "$ARCH" = "amd64" ]; then | |
| STUB_NAME="linuxx64.efi.stub" | |
| else | |
| STUB_NAME="linuxaa64.efi.stub" | |
| fi | |
| STUB_PATH="usr/lib/systemd/boot/efi/${STUB_NAME}" | |
| tar -xf "${DIR}/${ASSET_BASE}.tar" -C "${DIR}" "${STUB_PATH}" | |
| EFI_STUB="${DIR}/${STUB_PATH}" | |
| # Build initrd-uki by embedding squashfs into initrd | |
| cp "${DIR}/initrd" "${DIR}/initrd-uki" | |
| (cd "${DIR}" && echo root.squashfs | cpio -H newc -o | xz --check=crc32) >> "${DIR}/initrd-uki" | |
| CMDLINE="initrd=initrd gl.ovl=/:tmpfs gl.live=1 ip=any console=ttyS0,115200 console=tty0 earlyprintk=ttyS0,115200 consoleblank=0 ignition.firstboot=1 ignition.config.url=http://boot.onmetal.de:8083/ignition ignition.config.url.append.uuid=true ignition.platform.id=metal" | |
| ukify build \ | |
| --linux "${DIR}/vmlinuz" \ | |
| --initrd "${DIR}/initrd-uki" \ | |
| --stub "$EFI_STUB" \ | |
| --cmdline "$CMDLINE" \ | |
| --output "${DIR}/uki.img" | |
| echo "Built UKI for ${ARCH}" | |
| done | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: 'stable' | |
| - name: Install ironcore-image | |
| run: | | |
| go install github.com/ironcore-dev/ironcore-image/cmd@latest | |
| mv "$(go env GOPATH)/bin/cmd" "$(go env GOPATH)/bin/ironcore-image" | |
| echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" | |
| - name: Build image (KVM) | |
| if: matrix.variant == 'kvm' | |
| run: | | |
| ironcore-image build \ | |
| --tag "${IMAGE}:${VERSION}-kvm" \ | |
| --config "arch=amd64,rootfs=${KVM_AMD64_RAW},cmdline=./amd64.cmdline" \ | |
| --config "arch=arm64,rootfs=${KVM_ARM64_RAW},cmdline=./arm64.cmdline" | |
| - name: Build image (Metal) | |
| if: matrix.variant == 'metal' | |
| run: | | |
| ironcore-image build \ | |
| --tag "${IMAGE}:${VERSION}-metal" \ | |
| --config "arch=amd64,squashfs=${METAL_AMD64_DIR}/root.squashfs,initramfs=${METAL_AMD64_DIR}/initrd,kernel=${METAL_AMD64_DIR}/vmlinuz,uki=${METAL_AMD64_DIR}/uki.img" \ | |
| --config "arch=arm64,squashfs=${METAL_ARM64_DIR}/root.squashfs,initramfs=${METAL_ARM64_DIR}/initrd,kernel=${METAL_ARM64_DIR}/vmlinuz,uki=${METAL_ARM64_DIR}/uki.img" | |
| - name: Push image | |
| run: | | |
| ironcore-image push "${IMAGE}:${VERSION}-${{ matrix.variant }}" --push-sub-manifests |