name: Reusable Build and Push on: workflow_call: inputs: image_flavors: description: "JSON string of flavors to build, '[main, nvidia-open]'" default: "['main','nvidia-open']" type: string brand_name: description: "The Brand Name: aurora" required: false default: aurora type: string stream_name: description: "The Fedora Version: stable, or latest" required: true type: string architecture: description: "JSON string of architectures to build, '[aarch64, x86_64]'" default: "['x86_64']" required: false type: string publish: description: "Publish this image" required: false type: boolean default: true secrets: SIGNING_SECRET: required: true env: IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} concurrency: group: ${{ github.workflow }}-${{ github.ref || github.run_id }}-${{ inputs.brand_name}}-${{ inputs.stream_name }} cancel-in-progress: true permissions: {} jobs: build_container: name: image runs-on: ${{ matrix.architecture == 'x86_64' && 'ubuntu-26.04' || 'ubuntu-26.04-arm' }} permissions: contents: read packages: write id-token: write attestations: write artifact-metadata: write continue-on-error: false strategy: fail-fast: false matrix: image_flavor: ${{ fromJson(inputs.image_flavors) }} base_name: ["${{ inputs.brand_name }}", "${{ inputs.brand_name }}-dx"] stream_name: ["${{ inputs.stream_name }}"] architecture: ${{ fromJson(inputs.architecture) }} exclude: # DX is "legacy", don't build them for arm - architecture: aarch64 base_name: "${{ inputs.brand_name }}-dx" steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Free Up Space id: free-disk-space uses: hastd/free-disk-space@68572aeaadb7f76bd408246328e95926323402b5 # v0.1.2 with: # Newer runners ship with ~92G of free storage, so we can propably skip cleanup most of the time. skip-if-available: "80" - name: Configure containers policy for build run: | sudo ./.github/setup-runner-keys.sh - name: Install Just run: | /home/linuxbrew/.linuxbrew/bin/brew install just echo "/home/linuxbrew/.linuxbrew/bin" >> $GITHUB_PATH # FIXME: Implement retries for stuff like this - name: Install Cosign uses: sigstore/cosign-installer@756fe2d284c7292c4e9b62c8a28f1e5629c59532 # v4.1.3 # be careful when upgrading major versions with: cosign-release: 'v3.1.4' - name: Install ORAS uses: oras-project/setup-oras@5e2f2482b30b0cb3e14e67f89a80c88c659a7c63 # v2.0.3 - name: Check Just Syntax shell: bash run: | just --version just check - name: Image Name shell: bash env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} run: | IMAGE_NAME="$(just image_name \ --image ${MATRIX_BASE_NAME} \ --tag ${MATRIX_STREAM_NAME} \ --flavor ${MATRIX_IMAGE_FLAVOR})" echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV - name: DNF Package Cache Pull shell: bash id: pull-cache env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} GITHUB_EVENT_NAME: ${{ github.event_name }} run: | $(command -v just) \ setup-cache \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --ghcr \ --registry "${IMAGE_REGISTRY}" \ --pull - name: Build Image id: build-image shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # ghcurl MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} run: | $(command -v just) \ repo_organization="${GITHUB_REPOSITORY_OWNER}" \ build \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --ghcr \ --retry-pull - name: Write new DNF package cache id: push-cache shell: bash if: inputs.publish env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} GITHUB_EVENT_NAME: ${{ github.event_name }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | $(command -v just) \ setup-cache \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --ghcr \ --registry "${IMAGE_REGISTRY}" \ --github-event "${GITHUB_EVENT_NAME}" \ --push - name: Rechunk Image with Chunkah id: rechunker env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} run: | $(command -v just) rechunk \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" - name: Setup Syft id: setup-syft if: inputs.publish uses: anchore/sbom-action/download-syft@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3 - name: Generate SBOM if: inputs.publish id: generate-sbom env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} SYFT_CMD: ${{ steps.setup-syft.outputs.cmd }} run: | $(command -v just) gen-sbom \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ "${SYFT_CMD}" - name: Secureboot Check id: secureboot shell: bash env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} run: | $(command -v just) secureboot \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" - name: Export to OCI Archive if: github.event_name == 'pull_request' id: oci-archive env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} run: | $(command -v just) export-oci \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" - name: Upload OCI Archive as Artifact if: github.event_name == 'pull_request' uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: ${{ env.IMAGE_NAME }}-${{ matrix.architecture }}.oci path: ${{ env.IMAGE_NAME }}-${{ matrix.architecture }}.oci archive: false if-no-files-found: error retention-days: 1 - name: Create Bootable Image id: bootc-install # doing other images is not worth it if: github.event_name == 'pull_request' && matrix.base_name == 'aurora' && matrix.image_flavor == 'main' # free disk space varies continue-on-error: true env: MATRIX_BASE_NAME: ${{ matrix.base_name }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }} run: | $(command -v just) load-rootful \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ sudo $(command -v just) disk-image \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --ghcr - name: PR Testing Instructions if: github.event_name == 'pull_request' id: pr-summary env: IMAGE_NAME: ${{ env.IMAGE_NAME }} MATRIX_STREAM_NAME: "${{ matrix.stream_name }}" run: | echo "Download the .oci file" >> $GITHUB_STEP_SUMMARY echo "Rebase: sudo bootc switch --transport oci-archive /path/to/${IMAGE_NAME}".oci >> $GITHUB_STEP_SUMMARY echo "Go back to the production image e.g.: sudo bootc switch --enforce-container-sigpolicy ${IMAGE_REGISTRY}/${IMAGE_NAME}:${MATRIX_STREAM_NAME}" >> $GITHUB_STEP_SUMMARY - name: Generate tags id: generate-tags shell: bash env: MATRIX_BASE_NAME: "${{ matrix.base_name }}" MATRIX_STREAM_NAME: "${{ matrix.stream_name }}" MATRIX_IMAGE_FLAVOR: "${{ matrix.image_flavor }}" IMAGE_NAME: ${{ env.IMAGE_NAME }} GITHUB_EVENT_NAME: ${{ github.event_name }} GITHUB_EVENT_NUMBER: ${{ github.event.number }} run: | alias_tags="$(just generate-build-tags \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --ghcr \ --github-event "${GITHUB_EVENT_NAME}" \ --github-number "${GITHUB_EVENT_NUMBER}")" echo "Tags for this Action..." echo "$alias_tags" echo "alias_tags=${alias_tags}" >> $GITHUB_OUTPUT # Tag Images - name: Tag Images shell: bash env: IMAGE_NAME: ${{ env.IMAGE_NAME }} MATRIX_STREAM_NAME: ${{ matrix.stream_name }} ALIAS_TAGS: ${{ steps.generate-tags.outputs.alias_tags }} run: | set -eoux pipefail $(command -v just) tag-images \ --image "${IMAGE_NAME}" \ --default-tag "${MATRIX_STREAM_NAME}" \ --tags "${ALIAS_TAGS}" - name: Login to GitHub Container Registry if: inputs.publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_ACTOR: ${{ github.actor }} run: | just login-registry podman ghcr.io # cosign gets credentials from docker just login-registry docker ghcr.io # Workaround bug where capital letters in your GitHub username make it impossible to push to GHCR. # https://github.com/macbre/push-to-ghcr/issues/12 - name: Lowercase Registry if: inputs.publish env: IMAGE_REGISTRY: ${{ env.IMAGE_REGISTRY }} run: | echo "IMAGE_REGISTRY=${IMAGE_REGISTRY,,}" >> $GITHUB_OUTPUT # Workaround bugs caused by pushing images but not signing them because of CI flakes # FIXME: once https://github.com/containers/podman/issues/27796 resolved - name: Push Ephemeral Tag if: inputs.publish id: pre-push env: MATRIX_BASE_NAME: "${{ matrix.base_name }}" MATRIX_IMAGE_FLAVOR: "${{ matrix.image_flavor }}" MATRIX_STREAM_NAME: "${{ matrix.stream_name }}" TEMP_PUSH_TAG: "staging" run: | set -euox pipefail $(command -v just) push-image \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --ghcr \ --registry "${IMAGE_REGISTRY}" \ --temp-push \ --temp-push-tag "${TEMP_PUSH_TAG}" digest=$(< /tmp/digestfile) echo "digest=${digest}" >> $GITHUB_OUTPUT echo '## Temporary digest'>> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" echo "${digest}" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" - name: Sign container image if: inputs.publish env: DIGEST: ${{ steps.pre-push.outputs.digest }} COSIGN_EXPERIMENTAL: false COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} IMAGE_NAME: ${{ env.IMAGE_NAME }} run: | # use old bundle format, so we can use newer cosign 3.X.X instead of 2.X.X # https://github.com/containers/container-libs/issues/388 # https://github.com/coreos/rpm-ostree/issues/5509 cosign sign -y --new-bundle-format=false --use-signing-config=false --key env://COSIGN_PRIVATE_KEY ${IMAGE_REGISTRY}/${IMAGE_NAME}@${DIGEST} - name: Push to GHCR id: push if: inputs.publish env: MATRIX_BASE_NAME: "${{ matrix.base_name }}" MATRIX_STREAM_NAME: "${{ matrix.stream_name }}" MATRIX_IMAGE_FLAVOR: "${{ matrix.image_flavor }}" run: | set -euox pipefail $(command -v just) push-image \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --ghcr \ --registry "${IMAGE_REGISTRY}" digest=$(< /tmp/digestfile) echo "digest=${digest}" >> $GITHUB_OUTPUT echo '## Production digest'>> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" echo "${digest}" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" - name: Login to GitHub Container Registry with ORAS if: inputs.publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_ACTOR: ${{ github.actor }} run: | just login-registry oras ghcr.io - name: Attach SBOM if: inputs.publish id: attach-sbom env: MATRIX_BASE_NAME: "${{ matrix.base_name }}" MATRIX_STREAM_NAME: "${{ matrix.stream_name }}" MATRIX_IMAGE_FLAVOR: "${{ matrix.image_flavor }}" DIGEST: ${{ steps.pre-push.outputs.digest }} run: | set -euox pipefail $(command -v just) attach-sbom \ --image "${MATRIX_BASE_NAME}" \ --tag "${MATRIX_STREAM_NAME}" \ --flavor "${MATRIX_IMAGE_FLAVOR}" \ --registry "${IMAGE_REGISTRY}" \ --digest "${DIGEST}" sbom_digest=$(< /tmp/sbom-digestfile) echo "sbom_digest=${sbom_digest}" >> $GITHUB_OUTPUT - name: Sign SBOM OCI Artifact if: inputs.publish env: COSIGN_EXPERIMENTAL: false COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} IMAGE_NAME: ${{ env.IMAGE_NAME }} SBOM_DIGEST: ${{ steps.attach-sbom.outputs.sbom_digest }} run: | cosign sign -y --use-signing-config=false --key env://COSIGN_PRIVATE_KEY ${IMAGE_REGISTRY}/${IMAGE_NAME}@${SBOM_DIGEST} - name: Attestation if: inputs.publish uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2 with: subject-name: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} subject-digest: ${{ steps.pre-push.outputs.digest }} # Keep this disabled, this confuses cosign verify push-to-registry: false - name: Disk usage summary if: always() run: | echo "### Disk usage" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" df -h >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" check: name: Check all ${{ matrix.stream_name }} builds successful if: always() runs-on: ubuntu-slim needs: [build_container] steps: - name: Check Jobs env: JOBS: ${{ toJson(needs) }} run: | echo "Job status:" echo $JOBS | jq -r 'to_entries[] | " - \(.key): \(.value.result)"' for i in $(echo $JOBS | jq -r 'to_entries[] | .value.result'); do if [ "$i" != "success" ] && [ "$i" != "skipped" ]; then echo "" echo "Status check not okay!" exit 1 fi done