chore: update workflow to support multi-architecture builds #156
This file contains 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 Image | |
on: | |
pull_request: | |
branches: | |
- main | |
schedule: | |
- cron: "05 10 * * *" # 10:05am UTC everyday | |
merge_group: | |
push: | |
branches: | |
- main | |
paths-ignore: | |
- "**/README.md" | |
workflow_dispatch: | |
env: | |
IMAGE_NAME: "rs-main-test" # the name of the image produced by this build, matches repo names | |
IMAGE_DESC: "CentOS Stream-based image for basing off of " | |
IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" | |
DEFAULT_TAG: "latest" | |
CENTOS_VERSION: "stream10" | |
PLATFORMS: "amd64" | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref || github.run_id }} | |
cancel-in-progress: true | |
jobs: | |
generate_matrix: | |
runs-on: ubuntu-latest | |
outputs: | |
matrix: ${{ steps.set-matrix.outputs.matrix }} | |
steps: | |
- name: Set matrix | |
id: set-matrix | |
run: | | |
# turn the comma separated string into a list | |
platforms=(${{ env.PLATFORMS }}) | |
MATRIX="{\"include\":[]}" | |
for platform in "${platforms[@]}"; do | |
MATRIX=$(echo $MATRIX | jq ".include += [{\"platform\": \"$platform\"}]") | |
done | |
echo "matrix=$(echo $MATRIX | jq -c '.')" >> $GITHUB_OUTPUT | |
build_push: | |
name: Build and push image | |
runs-on: ubuntu-24.04 | |
needs: generate_matrix | |
strategy: | |
fail-fast: false | |
matrix: ${{fromJson(needs.generate_matrix.outputs.matrix)}} | |
permissions: | |
contents: read | |
packages: write | |
id-token: write | |
steps: | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | |
- name: Setup Just | |
uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2 | |
- name: Check Just Syntax | |
shell: bash | |
run: | | |
just check | |
# - name: Maximize build space | |
# uses: ublue-os/remove-unwanted-software@517622d6452028f266b7ba4cc9a123b5f58a6b53 # v7 | |
# with: | |
# remove-codeql: true | |
- name: Build Image | |
id: build-image | |
shell: bash | |
run: | | |
just=$(which just) | |
sudo $just build "${IMAGE_NAME}" "${DEFAULT_TAG}" | |
# Reprocess raw-img using rechunker which will delete it | |
- name: Run Rechunker | |
id: rechunk | |
uses: hhd-dev/rechunk@602e6d62558ab23e15e8764ce06e26c0f328da71 # v1.0.1 | |
with: | |
rechunk: "ghcr.io/hhd-dev/rechunk:v1.0.1" | |
ref: "localhost/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }}" | |
prev-ref: "${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }}" | |
skip_compression: true | |
version: ${{ env.CENTOS_VERSION }} | |
- name: Load in podman | |
run: | | |
IMAGE=$(podman pull ${{ steps.rechunk.outputs.ref }}) | |
sudo rm -rf ${{ steps.rechunk.outputs.location }} | |
podman image tag $IMAGE ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} | |
IMAGE=${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} | |
IMAGE_DIGEST=$(podman image inspect --format '{{.Digest}}' $IMAGE) | |
echo "image=$IMAGE" >> $GITHUB_OUTPUT | |
echo "digest=$IMAGE_DIGEST" >> $GITHUB_OUTPUT | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # 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 }} | |
# Push the image to GHCR (Image Registry) | |
- name: Push To GHCR | |
# if: github.event_name != 'pull_request' | |
id: push | |
env: | |
IMAGE_REGISTRY: ${{ steps.registry_case.outputs.lowercase }} | |
IMAGE_NAME: ${{ env.IMAGE_NAME }} | |
IMAGE_DIGEST: ${{ steps.load.outputs.digest }} | |
run: | | |
for i in {1..3}; do | |
podman push --remove-signatures --digestfile=/tmp/digestfile $IMAGE_REGISTRY/$IMAGE_NAME@${IMAGE_DIGEST} && break || sleep $((5 * i)); | |
done | |
REMOTE_IMAGE_DIGEST=$(cat /tmp/digestfile) | |
echo "remote_image_digest=$REMOTE_IMAGE_DIGEST" >> $GITHUB_OUTPUT | |
# This section is optional and only needs to be enabled in you plan on distributing | |
# your project to others to consume. You will need to create a public and private key | |
# using Cosign and save the private key as a repository secret in Github for this workflow | |
# to consume. For more details, review the image signing section of the README. | |
- name: Install Cosign | |
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 | |
#if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
- name: Sign container image | |
#if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
run: | | |
IMAGE_FULL="${{ env.IMAGE_REGISTRY }}/${IMAGE_NAME}" | |
cosign sign -y --key env://COSIGN_PRIVATE_KEY ${IMAGE_FULL}@${{ steps.push.outputs.remote_image_digest }} | |
env: | |
TAGS: ${{ steps.push.outputs.digest }} | |
COSIGN_EXPERIMENTAL: false | |
COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | |
- name: Create job outputs | |
env: | |
IMAGE_NAME: ${{ env.IMAGE_NAME }} | |
PLATFORM: ${{ matrix.platform }} | |
DIGEST: ${{ steps.push.outputs.remote_image_digest }} | |
run: | | |
mkdir -p /tmp/outputs/digests | |
echo "${DIGEST}" > /tmp/outputs/digests/${IMAGE_NAME}-${PLATFORM}.txt | |
- name: Upload Output Artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ env.IMAGE_NAME }}-${{ matrix.platform }} | |
retention-days: 1 | |
if-no-files-found: error | |
path: | | |
/tmp/outputs/digests/*.txt | |
manifest: | |
runs-on: ubuntu-latest | |
needs: | |
- build_push | |
permissions: | |
contents: read | |
packages: write | |
id-token: write | |
steps: | |
- name: Get current date | |
id: date | |
run: | | |
# Should generate 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 | |
- name: Image Metadata | |
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5 | |
id: metadata | |
with: | |
tags: | | |
type=raw,value=latest | |
type=raw,value=latest.{{date 'YYYYMMDD'}} | |
type=raw,value={{date 'YYYYMMDD'}} | |
type=raw,value=${{ env.CENTOS_VERSION }} | |
type=raw,value=${{ env.CENTOS_VERSION }}.{{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 }}/refs/heads/main/README.md | |
org.opencontainers.image.created=${{ steps.date.outputs.date }} | |
org.opencontainers.image.description=${{ env.IMAGE_DESC }} | |
org.opencontainers.image.documentation=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} | |
org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/blob/main/Containerfile | |
org.opencontainers.image.title=${{ env.IMAGE_NAME }} | |
org.opencontainers.image.url=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} | |
org.opencontainers.image.vendor=${{ github.repository_owner }} | |
org.opencontainers.image.version=${{ env.CENTOS_VERSION }} | |
io.artifacthub.package.deprecated=false | |
io.artifacthub.package.keywords=bootc,centos,ublue,universal-blue | |
io.artifacthub.package.license=Apache-2.0 | |
io.artifacthub.package.logo-url=https://avatars.githubusercontent.com/u/120078124?s=200&v=4 | |
io.artifacthub.package.maintainers=[{\"name\":\"tulilirockz\",\"email\":\"[email protected]\"},{\"name\":\"castrojo\", \"email\": \"[email protected]\"}] | |
io.artifacthub.package.prerelease=true | |
containers.bootc=1 | |
sep-tags: " " | |
sep-annotations: " " | |
- name: Fetch Build Outputs | |
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 | |
with: | |
pattern: ${{ env.IMAGE_NAME }}-*.txt | |
merge-multiple: true | |
path: /tmp/artifacts | |
- name: Load Outputs | |
id: load-outputs | |
run: | | |
DIGESTS_JSON=$(jq -n '{}') | |
for file in /tmp/artifacts/*.txt; do | |
# Extract the platform from the file name | |
PLATFORM=$(basename $digest_file | rev | cut -d'-' -f1 | rev | cut -d'.' -f1) | |
DIGEST=$(cat $digest_file) | |
# Add the platform and digest to the JSON object | |
DIGESTS_JSON=$(echo "$DIGESTS_JSON" | jq --arg key "$PLATFORM" --arg value "$DIGEST" '. + {($key): $value}') | |
done | |
echo "DIGESTS_JSON=$(echo $DIGESTS_JSON | jq -c '.')" >> $GITHUB_OUTPUT | |
- name: Create Manifest | |
id: create-manifest | |
run: | | |
podman manifest create ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} | |
echo "MANIFEST=${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_OUTPUT | |
- name: Populate Manifest | |
env: | |
MANIFEST: ${{ steps.create-manifest.outputs.MANIFEST }} | |
DIGESTS_JSON: ${{ steps.load-outputs.outputs.DIGESTS_JSON }} | |
run: | | |
DIGESTS=$(echo "$DIGESTS_JSON" | jq -c '.') | |
PLATFORMS=(${{ env.PLATFORMS }}) | |
for platform in ${PLATFORMS[@]}; do | |
digest=$(echo $DIGESTS | jq -r ".$platform") | |
podman manifest add $MANIFEST ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:$digest --arch $platform | |
done | |
- name: Login to GHCR | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Tag Manifest | |
id: tag-manifest | |
env: | |
TAGS: ${{ steps.metadata.outputs.tags }} | |
run: | | |
MANIFEST_TAGS=() | |
for tag in $(echo $TAGS | tr ' ' '\n'); do | |
podman manifest tag ${{ steps.create-manifest.outputs.MANIFEST }} ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:$tag | |
MANIFEST_TAGS+=(${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:$tag) | |
done | |
MANIFEST_TAGS_JSON=$(printf '%s\n' "${MANIFEST_TAGS[@]}" | jq -R . | jq -cs .) | |
echo "MANIFEST_TAGS_JSON=${MANIFEST_TAGS_JSON}" >> $GITHUB_OUTPUT | |
- name: Push Manifest | |
env: | |
MANIFEST: ${{ steps.create-manifest.outputs.MANIFEST }} | |
MANIFEST_TAGS_JSON: ${{ steps.tag-manifest.outputs.MANIFEST_TAGS_JSON }} | |
run: | | |
for tag in $(echo $MANIFEST_TAGS_JSON | jq -r '.[]'); do | |
podman manifest push --all=false $MANIFEST $tag | |
done |