ci: add input for pushing image #1
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: Reusable Build and Push | ||
|
Check failure on line 1 in .github/workflows/reusable-build.yml
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| image_flavors: | ||
| description: "JSON string of flavors to build, '[main, nvidia, hwe, hwe-nvidia]'" | ||
| default: "['main', 'nvidia', 'nvidia-open']" | ||
| type: string | ||
| brand_name: | ||
| description: "The Brand Name: bluefin" | ||
| required: true | ||
| default: bluefin | ||
| type: string | ||
| stream_name: | ||
| description: "The Fedora Version: gts, stable, or latest" | ||
| required: true | ||
| type: string | ||
| kernel_pin: | ||
| description: "The full kernel version to pin" | ||
| type: string | ||
| push: | ||
| description: "Whether the image should be pushed to a registry" | ||
| type: string | ||
| default: false | ||
| env: | ||
| IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} | ||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref || github.run_id }}-${{ inputs.brand_name}}-${{ inputs.stream_name }} | ||
| cancel-in-progress: true | ||
| jobs: | ||
| build_container: | ||
| name: image | ||
| runs-on: ubuntu-24.04 | ||
| 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 }}"] | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 | ||
| - name: Maximize build space | ||
| uses: ublue-os/container-storage-action@911baca08baf30c8654933e9e9723cb399892140 | ||
| continue-on-error: true | ||
| with: | ||
| target-dir: /var/lib/containers | ||
| mount-opts: compress-force=zstd:2 | ||
| loopback-free: 0.9 | ||
| - name: Setup Just | ||
| uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 | ||
| - name: Check Just Syntax | ||
| shell: bash | ||
| run: | | ||
| just check | ||
| - name: Image Name | ||
| shell: bash | ||
| run: | | ||
| IMAGE_NAME="$(just image_name ${{ matrix.base_name }} ${{ matrix.stream_name }} ${{ matrix.image_flavor }})" | ||
| echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV | ||
| - name: Default Tag | ||
| shell: bash | ||
| run: | | ||
| DEFAULT_TAG="$(just generate-default-tag \ | ||
| ${{ matrix.stream_name }} \ | ||
| "1")" | ||
| echo "Default Tag: ${DEFAULT_TAG}" | ||
| echo "DEFAULT_TAG=${DEFAULT_TAG}" >> $GITHUB_ENV | ||
| - name: Build Image | ||
| id: build-image | ||
| shell: bash | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| sudo -E $(command -v just) repo_organization="${{ github.repository_owner }}" \ | ||
| build-ghcr "${{ matrix.base_name }}" \ | ||
| "${{ matrix.stream_name }}" \ | ||
| "${{ matrix.image_flavor }}" \ | ||
| "${{ inputs.kernel_pin }}" | ||
| - name: Setup Syft | ||
| id: setup-syft | ||
| if: false | ||
| uses: anchore/sbom-action/download-syft@aa0e114b2e19480f157109b9922bda359bd98b90 # v0 | ||
| with: | ||
| syft-version: v1.20.0 | ||
| - name: Generate SBOM | ||
| if: false | ||
| id: generate-sbom | ||
| env: | ||
| IMAGE: ${{ env.IMAGE_NAME }} | ||
| STREAM_NAME: ${{ matrix.stream_name }} | ||
| SYFT_CMD: ${{ steps.setup-syft.outputs.cmd }} | ||
| run: | | ||
| sudo systemctl start podman.socket | ||
| OUTPUT_PATH="$(mktemp -d)/sbom.json" | ||
| export SYFT_PARALLELISM=$(($(nproc)*2)) | ||
| sudo $SYFT_CMD ${IMAGE}:${STREAM_NAME} -o spdx-json=${OUTPUT_PATH} | ||
| echo "OUTPUT_PATH=${OUTPUT_PATH}" >> $GITHUB_OUTPUT | ||
| - name: Rechunk Image | ||
| id: rechunk-image | ||
| shell: bash | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| sudo -E $(command -v just) rechunk "${{ matrix.base_name }}" \ | ||
| "${{ matrix.stream_name }}" \ | ||
| "${{ matrix.image_flavor }}" \ | ||
| "1" | ||
| - name: Load Image into Podman | ||
| id: load-rechunk | ||
| shell: bash | ||
| run: | | ||
| sudo -E $(command -v just) load-rechunk "${{ matrix.base_name }}" \ | ||
| "${{ env.DEFAULT_TAG }}" \ | ||
| "${{ matrix.image_flavor }}" | ||
| - name: Secureboot Check | ||
| id: secureboot | ||
| shell: bash | ||
| run: | | ||
| sudo -E $(command -v just) secureboot "${{ matrix.base_name }}" \ | ||
| "${{ env.DEFAULT_TAG }}" \ | ||
| "${{ matrix.image_flavor }}" | ||
| - name: Generate tags | ||
| id: generate-tags | ||
| shell: bash | ||
| run: | | ||
| alias_tags="$(just generate-build-tags \ | ||
| "${{ matrix.base_name }}" \ | ||
| "${{ matrix.stream_name }}" \ | ||
| "${{ matrix.image_flavor }}" \ | ||
| "${{ inputs.kernel_pin }}" \ | ||
| "1" \ | ||
| "$(podman inspect ${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} | jq -r '.[].Config.Labels["org.opencontainers.image.version"]')" \ | ||
| "${{ github.event_name }}" \ | ||
| "${{ 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 | ||
| run: | | ||
| set -eoux pipefail | ||
| sudo -E $(command -v just) tag-images "${{ env.IMAGE_NAME }}" \ | ||
| "${{ env.DEFAULT_TAG }}" \ | ||
| "${{ steps.generate-tags.outputs.alias_tags }}" | ||
| # 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 | ||
| id: registry_case | ||
| uses: ASzc/change-string-case-action@d0603cd0a7dd490be678164909f65c7737470a7f # v6 | ||
| with: | ||
| string: ${{ env.IMAGE_REGISTRY }} | ||
| - name: Login to GitHub Container Registry | ||
| if: inputs.push == "true" | ||
| run: | | ||
| echo ${{ secrets.GITHUB_TOKEN }} | podman login ghcr.io -u ${{ github.actor }} --password-stdin | ||
| echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin | ||
| - name: Push to GHCR | ||
| id: push | ||
| if: inputs.push == "true" | ||
| uses: Wandalen/wretry.action@e68c23e6309f2871ca8ae4763e7629b9c258e1ea # v3.8.0 | ||
| with: | ||
| attempt_limit: 3 | ||
| attempt_delay: 15000 | ||
| command: | | ||
| set -euox pipefail | ||
| for tag in ${{ steps.generate-tags.outputs.alias_tags }}; do | ||
| sudo -E podman push ${{ env.IMAGE_NAME }}:${tag} ${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}:${tag} | ||
| done | ||
| if [[ "${{ matrix.image_flavor }}" =~ hwe ]]; then | ||
| image_name="${{ env.IMAGE_NAME }}" | ||
| asus_name="${image_name/hwe/asus}" | ||
| surface_name="${image_name/hwe/surface}" | ||
| for tag in ${{ steps.generate-tags.outputs.alias_tags }}; do | ||
| sudo -E podman push ${asus_name}:${tag} ${{ steps.registry_case.outputs.lowercase }}/${asus_name}:${tag} | ||
| sudo -E podman push ${surface_name}:${tag} ${{ steps.registry_case.outputs.lowercase }}/${surface_name}:${tag} | ||
| done | ||
| fi | ||
| digest=$(skopeo inspect docker://${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} --format '{{.Digest}}') | ||
| echo "digest=${digest}" >> $GITHUB_OUTPUT | ||
| - name: Install Cosign | ||
| uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1 | ||
| if: github.event_name != 'pull_request' | ||
| - name: Sign container image | ||
| if: github.event_name != 'pull_request' | ||
| run: | | ||
| cosign sign -y --key env://COSIGN_PRIVATE_KEY ${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}@${TAGS} | ||
| env: | ||
| TAGS: ${{ steps.push.outputs.outputs && fromJSON(steps.push.outputs.outputs).digest }} | ||
| COSIGN_EXPERIMENTAL: false | ||
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | ||
| - name: Sign container image | ||
| if: inputs.push == "true" && contains(matrix.image_flavor, 'hwe') | ||
| shell: bash | ||
| run: | | ||
| image_name="${{ env.IMAGE_NAME }}" | ||
| asus_name="${image_name/hwe/asus}" | ||
| surface_name="${image_name/hwe/surface}" | ||
| cosign sign -y --key env://COSIGN_PRIVATE_KEY ${{ steps.registry_case.outputs.lowercase }}/${asus_name}@${TAGS} | ||
| cosign sign -y --key env://COSIGN_PRIVATE_KEY ${{ steps.registry_case.outputs.lowercase }}/${surface_name}@${TAGS} | ||
| env: | ||
| TAGS: ${{ steps.push.outputs.outputs && fromJSON(steps.push.outputs.outputs).digest }} | ||
| COSIGN_EXPERIMENTAL: false | ||
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | ||
| - name: Add SBOM Attestation | ||
| if: false | ||
| env: | ||
| IMAGE: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| DIGEST: ${{ steps.push.outputs.outputs && fromJSON(steps.push.outputs.outputs).digest }} | ||
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | ||
| SBOM_OUTPUT: ${{ steps.generate-sbom.outputs.OUTPUT_PATH }} | ||
| run: | | ||
| cd "$(dirname "$SBOM_OUTPUT")" | ||
| cosign attest -y \ | ||
| --predicate ./sbom.json \ | ||
| --type spdxjson \ | ||
| --key env://COSIGN_PRIVATE_KEY \ | ||
| "${IMAGE}@${DIGEST}" | ||
| check: | ||
| name: Check all ${{ matrix.stream_name }} builds successful | ||
| if: always() | ||
| runs-on: ubuntu-latest | ||
| 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 | ||