Build Latest #242
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 container image | |
| on: | |
| pull_request: | |
| branches: | |
| - master | |
| schedule: | |
| - cron: "05 11 * * *" # 11:05am UTC everyday | |
| push: | |
| tags: | |
| - stable | |
| - latest | |
| workflow_dispatch: | |
| env: | |
| IMAGE_DESC: "${{ github.event.repository.name }}" | |
| IMAGE_NAME: "${{ github.event.repository.name }}" # output image name, usually same as repo name | |
| IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" # do not edit | |
| DEFAULT_TAG: "latest" | |
| concurrency: | |
| # Group by commit SHA - same commit builds wait for each other | |
| group: ${{ github.workflow }}-${{ github.sha }} | |
| cancel-in-progress: false | |
| jobs: | |
| setup: | |
| name: Setup build matrix | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| channels: ${{ steps.channels.outputs.channels }} | |
| steps: | |
| - name: Determine channels to build | |
| id: channels | |
| run: | | |
| if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo 'channels=["latest","stable"]' >> $GITHUB_OUTPUT | |
| elif [[ "${{ github.event_name }}" == "push" ]]; then | |
| echo 'channels=["${{ github.ref_name }}"]' >> $GITHUB_OUTPUT | |
| else | |
| echo 'channels=["latest"]' >> $GITHUB_OUTPUT | |
| fi | |
| build_push: | |
| name: Build and push image (${{ matrix.variant }}/${{ matrix.channel }}) | |
| needs: setup | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| variant: [base, nvidia, nvidia-open] | |
| channel: ${{ fromJson(needs.setup.outputs.channels) }} | |
| include: | |
| - variant: base | |
| base_image: ghcr.io/pureblue-os/gnome:latest | |
| - variant: nvidia | |
| base_image: ghcr.io/pureblue-os/gnome-nvidia:latest | |
| - variant: nvidia-open | |
| base_image: ghcr.io/pureblue-os/gnome-nvidia-open:latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Prepare environment | |
| run: | | |
| # Lowercase the image registry | |
| echo "IMAGE_REGISTRY=${IMAGE_REGISTRY,,}" >> ${GITHUB_ENV} | |
| # Overwrite IMAGE_NAME with variant suffix | |
| if [[ "${{ matrix.variant }}" == "base" ]]; then | |
| echo "IMAGE_NAME=${IMAGE_NAME,,}" >> ${GITHUB_ENV} | |
| else | |
| echo "IMAGE_NAME=${IMAGE_NAME,,}-${{ matrix.variant }}" >> ${GITHUB_ENV} | |
| fi | |
| # Set BASE_IMAGE for build.sh | |
| echo "BASE_IMAGE=${{ matrix.base_image }}" >> ${GITHUB_ENV} | |
| - name: Get base image info | |
| id: base_image | |
| run: | | |
| # Inspect the base image to get version info | |
| BASE_INFO=$(skopeo inspect docker://${{ matrix.base_image }}) | |
| # Get the digest (sha256:xxx) and extract short hash | |
| BASE_DIGEST=$(echo "$BASE_INFO" | jq -r '.Digest' | cut -d: -f2) | |
| BASE_SHA="${BASE_DIGEST::8}" | |
| echo "base_sha=$BASE_SHA" >> $GITHUB_OUTPUT | |
| # Get Fedora version from org.opencontainers.image.version (e.g., "43.20260117.0" -> "43") | |
| FEDORA_VERSION=$(echo "$BASE_INFO" | jq -r '.Labels["org.opencontainers.image.version"]' | cut -d. -f1) | |
| echo "fedora_version=$FEDORA_VERSION" >> $GITHUB_OUTPUT | |
| - name: Checkout | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v5 | |
| with: | |
| ref: ${{ github.event_name == 'pull_request' && github.ref || matrix.channel }} | |
| - name: Set image version | |
| run: | | |
| COMMIT_SHA=$(git rev-parse HEAD) | |
| IMAGE_VERSION="${{ steps.base_image.outputs.fedora_version }}.${{ steps.base_image.outputs.base_sha }}.${COMMIT_SHA::8}" | |
| echo "IMAGE_VERSION=$IMAGE_VERSION" >> ${GITHUB_ENV} | |
| echo "Image version: $IMAGE_VERSION (channel: ${{ matrix.channel }}, commit: ${COMMIT_SHA::8})" | |
| - name: Get ISO 8601 timestamp for OCI image labels | |
| id: iso8601 | |
| run: | | |
| echo "timestamp=$(date -u +%Y\-%m\-%d\T%H\:%M\:%S\Z)" >> $GITHUB_OUTPUT | |
| - name: Image Metadata | |
| uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5 | |
| id: metadata | |
| with: | |
| tags: | | |
| type=raw,value=latest,enable=${{ matrix.channel == 'latest' }} | |
| type=raw,value=${{ steps.base_image.outputs.fedora_version }},enable=${{ matrix.channel == 'latest' }} | |
| type=raw,value=${{ steps.base_image.outputs.fedora_version }}.${{ steps.base_image.outputs.base_sha }},enable=${{ matrix.channel == 'latest' }} | |
| type=raw,value=stable,enable=${{ matrix.channel == 'stable' }} | |
| type=sha,enable=${{ github.event_name == 'pull_request' }} | |
| type=ref,event=pr | |
| labels: | | |
| org.opencontainers.image.created=${{ steps.iso8601.outputs.timestamp }} | |
| org.opencontainers.image.description=${{ env.IMAGE_DESC }} (${{ matrix.variant }}) | |
| org.opencontainers.image.documentation=https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/README.md | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| org.opencontainers.image.title=${{ env.IMAGE_NAME }} | |
| org.opencontainers.image.url=https://github.com/${{ github.repository }} | |
| org.opencontainers.image.vendor=${{ github.repository_owner }} | |
| org.opencontainers.image.version=${{ env.IMAGE_VERSION }} | |
| containers.bootc=1 | |
| sep-tags: " " | |
| sep-annotations: " " | |
| - name: Check if build and publish are needed | |
| id: checks | |
| run: | | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| echo "Pull request - will build but not publish" | |
| else | |
| echo "should_publish=true" >> $GITHUB_OUTPUT | |
| # Check if the commit tag is already present in the registry | |
| if skopeo inspect docker://${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }} >/dev/null 2>&1; then | |
| echo "should_build=false" >> $GITHUB_OUTPUT | |
| echo "Image for ${{ env.IMAGE_VERSION }} already exists, will retag only" | |
| else | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| echo "Image for ${{ env.IMAGE_VERSION }} not found, will build and publish" | |
| fi | |
| fi | |
| - name: Maximize build space | |
| if: steps.checks.outputs.should_build == 'true' | |
| uses: ublue-os/remove-unwanted-software@695eb75bc387dbcd9685a8e72d23439d8686cba6 | |
| with: | |
| extra-squeeze: true | |
| - name: Mount BTRFS for podman storage | |
| id: container-storage-action | |
| if: steps.checks.outputs.should_build == 'true' | |
| uses: ublue-os/container-storage-action@911baca08baf30c8654933e9e9723cb399892140 | |
| continue-on-error: true | |
| with: | |
| target-dir: /var/lib/containers | |
| mount-opts: compress-force=zstd:2 | |
| - name: Build Image | |
| id: build_image | |
| if: steps.checks.outputs.should_build == 'true' | |
| run: | | |
| sudo -E bash build.sh | |
| - name: Load rechunk action | |
| if: steps.checks.outputs.should_build == 'true' && steps.checks.outputs.should_publish == 'true' | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: hhd-dev/rechunk | |
| path: .rechunk | |
| ref: v1.1.2 | |
| - name: Rechunk image | |
| id: rechunk | |
| if: steps.checks.outputs.should_build == 'true' && steps.checks.outputs.should_publish == 'true' | |
| uses: ./.rechunk | |
| with: | |
| ref: localhost/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} | |
| version: ${{ env.IMAGE_VERSION }} | |
| revision: ${{ env.IMAGE_VERSION }} | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 | |
| if: steps.checks.outputs.should_publish == 'true' | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Push to GHCR | |
| if: steps.checks.outputs.should_build == 'true' && steps.checks.outputs.should_publish == 'true' | |
| env: | |
| REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Push rechunked image with the commit tag | |
| sudo skopeo copy \ | |
| --dest-creds=${{ github.actor }}:$REGISTRY_PASSWORD \ | |
| ${{ steps.rechunk.outputs.ref }} \ | |
| docker://${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }} | |
| - name: Add tags to pushed image | |
| if: steps.checks.outputs.should_publish == 'true' | |
| id: tag | |
| env: | |
| REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| SOURCE="${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }}" | |
| echo "Tagging from $SOURCE" | |
| # Add all tags | |
| for tag in ${{ steps.metadata.outputs.tags }}; do | |
| echo "Adding tag: $tag" | |
| skopeo copy \ | |
| --dest-creds=${{ github.actor }}:$REGISTRY_PASSWORD \ | |
| --src-creds=${{ github.actor }}:$REGISTRY_PASSWORD \ | |
| docker://$SOURCE \ | |
| docker://${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:$tag | |
| done | |
| DIGEST=$(skopeo inspect --creds=${{ github.actor }}:$REGISTRY_PASSWORD docker://$SOURCE --format '{{.Digest}}') | |
| echo "digest=$DIGEST" >> $GITHUB_OUTPUT | |
| echo "registry-path=${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_OUTPUT | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 | |
| if: steps.checks.outputs.should_publish == 'true' | |
| - name: Sign container image | |
| if: steps.checks.outputs.should_publish == 'true' | |
| run: | | |
| REGISTRY_PATH="${{ steps.tag.outputs.registry-path }}" | |
| DIGEST="${{ steps.tag.outputs.digest }}" | |
| cosign sign -y --key env://COSIGN_PRIVATE_KEY ${REGISTRY_PATH}@${DIGEST} | |
| env: | |
| COSIGN_EXPERIMENTAL: false | |
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} |