Build container image #88
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
| --- | |
| # Single-job sequential build: base + K3s share local containers-storage so | |
| # the K3s `FROM ${BASE_IMAGE}` resolves without a registry round-trip. This | |
| # shape is the right answer for 2 images with a parent-child relationship. | |
| # | |
| # When you add a third leaf flavor (RKE2, MicroK8s, multi-arch, alternate | |
| # kernel streams, …), refactor to two jobs: | |
| # 1) build_base (one runner, pushes by digest) | |
| # 2) build_variants (matrix over flavors, pins base by @digest) | |
| # Full copy-paste-ready recipe + pitfalls: | |
| # docs/ci-build-matrix-refactor.md | |
| name: Build container image | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| schedule: | |
| - cron: '05 10 * * *' # 10:05am UTC everyday | |
| push: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - '**/README.md' | |
| workflow_dispatch: | |
| env: | |
| IMAGE_DESC: "My Customized Universal Blue Image" | |
| IMAGE_KEYWORDS: "bootc,ublue,universal-blue" | |
| IMAGE_LOGO_URL: "https://avatars.githubusercontent.com/u/120078124?s=200&v=4" # Put your own image here for a fancy profile on https://artifacthub.io/! | |
| 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: "stable" | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| build_push: | |
| name: Build and push image | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Prepare environment | |
| run: | | |
| # Lowercase the image uri | |
| echo "IMAGE_REGISTRY=${IMAGE_REGISTRY,,}" >> ${GITHUB_ENV} | |
| echo "IMAGE_NAME=${IMAGE_NAME,,}" >> ${GITHUB_ENV} | |
| # These stage versions are pinned by https://github.com/renovatebot/renovate | |
| - name: Checkout | |
| uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 | |
| # This is optional, but if you see that your builds are way too big for the runners, you can enable this by uncommenting the following lines: | |
| # - name: Maximize build space | |
| # uses: ublue-os/remove-unwanted-software@517622d6452028f266b7ba4cc9a123b5f58a6b53 # v7 | |
| # with: | |
| # remove-codeql: true | |
| - name: Mount BTRFS for podman storage | |
| id: container-storage-action | |
| uses: ublue-os/container-storage-action@911baca08baf30c8654933e9e9723cb399892140 # main | |
| # Fallback to the remove-unwanted-software-action if github doesn't allocate enough space | |
| # See: https://github.com/ublue-os/container-storage-action/pull/11 | |
| continue-on-error: true | |
| with: | |
| target-dir: /var/lib/containers | |
| mount-opts: compress-force=zstd:2 | |
| - name: Get current date | |
| id: date | |
| run: | | |
| # This generates a timestamp like what is defined on the ArtifactHub documentation | |
| # E.G: 2022-02-08T15:38:15Z' | |
| # https://artifacthub.io/docs/topics/repositories/container-images/ | |
| # https://linux.die.net/man/1/date | |
| echo "date=$(date -u +%Y\-%m\-%d\T%H\:%M\:%S\Z)" >> $GITHUB_OUTPUT | |
| # Image metadata for https://artifacthub.io/ - This is optional but is highly recommended so we all can get a index of all the custom images | |
| # The metadata by itself is not going to do anything, you choose if you want your image to be on ArtifactHub or not. | |
| - name: Image Metadata (base) | |
| uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5 | |
| id: metadata | |
| with: | |
| # This generates all the tags for your image, you can add custom tags here too! | |
| # Default tags are "$DEFAULT_TAG" and "$DEFAULT_TAG.$date". | |
| tags: | | |
| type=raw,value=${{ env.DEFAULT_TAG }} | |
| type=raw,value=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| type=raw,value={{date 'YYYYMMDD'}} | |
| type=sha,enable=${{ github.event_name == 'pull_request' }} | |
| type=ref,event=pr | |
| labels: | | |
| io.artifacthub.package.readme-url=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/README.md | |
| org.opencontainers.image.created=${{ steps.date.outputs.date }} | |
| org.opencontainers.image.description=${{ env.IMAGE_DESC }} | |
| org.opencontainers.image.documentation=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/README.md | |
| org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/blob/${{ github.sha }}/Containerfile | |
| org.opencontainers.image.title=${{ env.IMAGE_NAME }} | |
| org.opencontainers.image.url=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/tree/${{ github.sha }} | |
| org.opencontainers.image.vendor=${{ github.repository_owner }} | |
| org.opencontainers.image.version=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| io.artifacthub.package.deprecated=false | |
| io.artifacthub.package.keywords=${{ env.IMAGE_KEYWORDS }} | |
| io.artifacthub.package.license=Apache-2.0 | |
| io.artifacthub.package.logo-url=${{ env.IMAGE_LOGO_URL }} | |
| io.artifacthub.package.prerelease=false | |
| containers.bootc=1 | |
| sep-tags: " " | |
| sep-annotations: " " | |
| # Same shape as the base metadata, but with title/source/url pointing at | |
| # the K3s flavor so downstream tools (ArtifactHub, OCI inspectors, | |
| # `bootc status`) report the correct image identity. | |
| - name: Image Metadata (K3s) | |
| uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5 | |
| id: metadata_k3s | |
| with: | |
| tags: | | |
| type=raw,value=${{ env.DEFAULT_TAG }} | |
| type=raw,value=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| type=raw,value={{date 'YYYYMMDD'}} | |
| type=sha,enable=${{ github.event_name == 'pull_request' }} | |
| type=ref,event=pr | |
| labels: | | |
| io.artifacthub.package.readme-url=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/README.md | |
| org.opencontainers.image.created=${{ steps.date.outputs.date }} | |
| org.opencontainers.image.description=${{ env.IMAGE_DESC }} (K3s flavor) | |
| org.opencontainers.image.documentation=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/docs/k3s-rancher-vms.md | |
| org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/blob/${{ github.sha }}/Containerfile.k3s | |
| org.opencontainers.image.title=${{ env.IMAGE_NAME }}-k3s | |
| org.opencontainers.image.url=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/tree/${{ github.sha }} | |
| org.opencontainers.image.vendor=${{ github.repository_owner }} | |
| org.opencontainers.image.version=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| io.artifacthub.package.deprecated=false | |
| io.artifacthub.package.keywords=${{ env.IMAGE_KEYWORDS }},k3s,kubernetes | |
| io.artifacthub.package.license=Apache-2.0 | |
| io.artifacthub.package.logo-url=${{ env.IMAGE_LOGO_URL }} | |
| io.artifacthub.package.prerelease=false | |
| containers.bootc=1 | |
| sep-tags: " " | |
| sep-annotations: " " | |
| - name: Build Image | |
| id: build_image | |
| uses: redhat-actions/buildah-build@7a95fa7ee0f02d552a32753e7414641a04307056 # v2 | |
| with: | |
| containerfiles: | | |
| ./Containerfile | |
| # Postfix image name with -custom to make it a little more descriptive | |
| # Syntax: https://docs.github.com/en/actions/learn-github-actions/expressions#format | |
| image: ${{ env.IMAGE_NAME }} | |
| tags: ${{ env.DEFAULT_TAG }} | |
| labels: ${{ steps.metadata.outputs.labels }} | |
| oci: false | |
| # Tag for registry | |
| - name: Tag for registry | |
| run: | | |
| for tag in ${{ steps.metadata.outputs.tags }}; do | |
| podman tag ${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} ${{ env.IMAGE_NAME }}:$tag | |
| done | |
| # Build the K3s flavor on top of the freshly-built base. buildah picks | |
| # up `localhost/<image>:stable` from local containers-storage — no | |
| # registry round-trip needed. Containerfile.k3s honors BASE_IMAGE as | |
| # its FROM, so we just point at the local tag. | |
| - name: Build K3s flavor image | |
| id: build_image_k3s | |
| uses: redhat-actions/buildah-build@7a95fa7ee0f02d552a32753e7414641a04307056 # v2 | |
| with: | |
| containerfiles: | | |
| ./Containerfile.k3s | |
| image: ${{ env.IMAGE_NAME }}-k3s | |
| tags: ${{ env.DEFAULT_TAG }} | |
| labels: ${{ steps.metadata_k3s.outputs.labels }} | |
| build-args: | | |
| BASE_IMAGE=localhost/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} | |
| oci: false | |
| - name: Tag K3s flavor for registry | |
| run: | | |
| for tag in ${{ steps.metadata.outputs.tags }}; do | |
| podman tag ${{ env.IMAGE_NAME }}-k3s:${{ env.DEFAULT_TAG }} ${{ env.IMAGE_NAME }}-k3s:$tag | |
| done | |
| # SBOM (OPTIONAL): Software Bill of Materials generation using Syft | |
| # Creates a detailed inventory of all software components in the image for supply chain security. | |
| # To enable: | |
| # 1. Uncomment the "Setup Syft" step below | |
| # 2. Uncomment the "Generate SBOM" step below | |
| # 3. If using image signing, also uncomment the "Add SBOM Attestation" step further down | |
| # Documentation: https://github.com/anchore/syft | |
| # - name: Setup Syft | |
| # id: setup-syft | |
| # uses: anchore/sbom-action/download-syft@8e94d75ddd33f69f691467e42275782e4bfefe84 # v0.20.9 | |
| # with: | |
| # syft-version: v1.20.0 | |
| # | |
| # - name: Generate SBOM | |
| # id: generate-sbom | |
| # env: | |
| # IMAGE: ${{ env.IMAGE_NAME }} | |
| # DEFAULT_TAG: ${{ env.DEFAULT_TAG }} | |
| # SYFT_CMD: ${{ steps.setup-syft.outputs.cmd }} | |
| # run: | | |
| # OUTPUT_PATH="$(mktemp -d)/sbom.json" | |
| # export SYFT_PARALLELISM=$(($(nproc)*2)) | |
| # $SYFT_CMD ${IMAGE}:${DEFAULT_TAG} -o spdx-json=${OUTPUT_PATH} | |
| # echo "OUTPUT_PATH=${OUTPUT_PATH}" >> $GITHUB_OUTPUT | |
| # These `if` statements are so that pull requests for your custom images do not make it publish any packages under your name without you knowing | |
| # They also check if the runner is on the default branch so that things like the merge queue (if you enable it), are going to work | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Push To GHCR (base) | |
| uses: redhat-actions/push-to-registry@5ed88d269cf581ea9ef6dd6806d01562096bee9c # v2 | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| id: push | |
| env: | |
| REGISTRY_USER: ${{ github.actor }} | |
| REGISTRY_PASSWORD: ${{ github.token }} | |
| with: | |
| registry: ${{ env.IMAGE_REGISTRY }} | |
| image: ${{ env.IMAGE_NAME }} | |
| tags: ${{ steps.metadata.outputs.tags }} | |
| username: ${{ env.REGISTRY_USER }} | |
| password: ${{ env.REGISTRY_PASSWORD }} | |
| - name: Push To GHCR (K3s flavor) | |
| uses: redhat-actions/push-to-registry@5ed88d269cf581ea9ef6dd6806d01562096bee9c # v2 | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| id: push_k3s | |
| env: | |
| REGISTRY_USER: ${{ github.actor }} | |
| REGISTRY_PASSWORD: ${{ github.token }} | |
| with: | |
| registry: ${{ env.IMAGE_REGISTRY }} | |
| image: ${{ env.IMAGE_NAME }}-k3s | |
| tags: ${{ steps.metadata.outputs.tags }} | |
| username: ${{ env.REGISTRY_USER }} | |
| password: ${{ env.REGISTRY_PASSWORD }} | |
| # Image signing with Cosign. Signs every tag of both flavors using the | |
| # repository's SIGNING_SECRET. The corresponding public key (cosign.pub) | |
| # is baked into the image by build/10-build.sh, so consumers verify these | |
| # signatures on `bootc upgrade`/`switch` by default — no per-VM setup. | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| - name: Sign container images | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| env: | |
| COSIGN_EXPERIMENTAL: false | |
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | |
| run: | | |
| for image in "${{ env.IMAGE_NAME }}" "${{ env.IMAGE_NAME }}-k3s"; do | |
| FULL="${{ env.IMAGE_REGISTRY }}/${image}" | |
| for tag in ${{ steps.metadata.outputs.tags }}; do | |
| echo ">>> cosign sign ${FULL}:${tag}" | |
| cosign sign -y --key env://COSIGN_PRIVATE_KEY "${FULL}:${tag}" | |
| done | |
| done | |
| # OPTIONAL: SBOM Attestation | |
| # Attaches SBOM (Software Bill of Materials) to your signed image for supply chain security. | |
| # Requires image signing to be enabled first (see above). | |
| # To enable: | |
| # 1. First enable image signing (see above section) | |
| # 2. Uncomment the "Add SBOM Attestation" step below | |
| # Documentation: https://slsa.dev/spec/v1.0/requirements#provenance-available | |
| # - name: Add SBOM Attestation | |
| # if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| # env: | |
| # IMAGE: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} | |
| # DIGEST: ${{ steps.push.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 "$(basename "$SBOM_OUTPUT")" \ | |
| # --type spdxjson \ | |
| # --key env://COSIGN_PRIVATE_KEY \ | |
| # "${IMAGE}@${DIGEST}" |