Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update workflow to support multi-architecture builds #51

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e697ba4
chore: update workflow to support multi-architecture builds
p5 Jan 5, 2025
697a8f4
fix: correct justfile build image name
p5 Jan 5, 2025
2da1e03
chore: speed up dev builds
p5 Jan 5, 2025
f684ab8
fix: address rechunked image with correct names
p5 Jan 5, 2025
e181a06
revert: address rechunked image with correct names
p5 Jan 5, 2025
077afeb
fix: query full image name in skopeo
p5 Jan 5, 2025
958d153
fix: tell skopeo to look at local containers-storage
p5 Jan 5, 2025
cdd2709
fix: tell skopeo to use docker
p5 Jan 5, 2025
6d74b46
fix: switch out skopeo for podman
p5 Jan 5, 2025
4b0657b
fix: fetch registry from env
p5 Jan 5, 2025
b0e00e6
fix: do not push a digest
p5 Jan 5, 2025
50c609f
fix: push the image with platform appended
p5 Jan 5, 2025
5f533be
debug: output digestfile
p5 Jan 5, 2025
c5e94e5
fix: correct file variable name in outputs load step
p5 Jan 5, 2025
8caf4fc
fix: update artifact name to download
p5 Jan 5, 2025
27a654a
debug: output digest and arch
p5 Jan 5, 2025
6640bb2
fix: correct manifest image digest
p5 Jan 5, 2025
00747c9
fix: tag and push manifests in single step
p5 Jan 5, 2025
2b7c8ab
fix: push to correct registry and name
p5 Jan 5, 2025
fe7806e
chore: enable mock ARM builds
p5 Jan 5, 2025
c1ee35a
chore: try building in ubi9
p5 Jan 5, 2025
e074194
fix: remove sudo from dnf install
p5 Jan 5, 2025
5f48c6a
fix: no-op sudo
p5 Jan 5, 2025
c48ca99
fix: roll our own registry login step
p5 Jan 5, 2025
b6045d4
fix: hack to get cosign to use Podman creds
p5 Jan 5, 2025
5582c84
fix: create docker directory if not exists
p5 Jan 5, 2025
4707c22
chore: enable full build
p5 Jan 5, 2025
ae31f0c
chore: build single architecture in PRs
p5 Jan 5, 2025
6647aa1
chore: actually build ARM
p5 Jan 5, 2025
b7a373b
fix: builder runner logic
p5 Jan 5, 2025
44e0e9b
fix: make platforms space-separated
p5 Jan 5, 2025
09abead
chore: bump rechunk to v1.1.0
p5 Jan 5, 2025
1be021c
fix: create another directory Rechunk expects
p5 Jan 5, 2025
27b90ae
chore: finalise configuration
p5 Jan 5, 2025
035b7d5
chore: switch to centos:stream10 builder and add labels to manifest
p5 Jan 5, 2025
7483259
fix: install whereis on the runner
p5 Jan 5, 2025
53964ae
fix: install which on the runner
p5 Jan 5, 2025
da458fa
revert: go back to ubi9
p5 Jan 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 209 additions & 68 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,199 @@ on:
pull_request:
branches:
- main
schedule:
- cron: '05 10 * * *' # 10:05am UTC everyday
merge_group:
push:
branches:
- main
paths-ignore:
- '**/README.md'
- "**.md"
schedule:
- cron: "05 10 * * *" # 10:05am UTC everyday
merge_group:
workflow_dispatch:

env:
IMAGE_NAME: "main" # the name of the image produced by this build, matches repo names
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"

# While we are using self-hosted runners, please only build on ARM64 when the image will be pushed
# to a registry. This is because the runners are not free, and I want to protect my wallet.
#
# Thanks,
# Robert (p5)
PLATFORMS: ${{ github.event_name == 'pull_request' && 'amd64' || 'amd64 arm64' }}

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

runs-on: ${{ matrix.platform == 'amd64' && 'ubuntu-24.04' || format('runs-on,runner=1cpu-linux-{0},run-id={1}', matrix.platform, github.run_id) }}
needs: generate_matrix
timeout-minutes: 30
container:
image: redhat/ubi9:latest
options: --privileged
strategy:
fail-fast: false
matrix: ${{fromJson(needs.generate_matrix.outputs.matrix)}}
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Setup Container
run: |
dnf install -y \
git \
podman \
skopeo

# Rechunk requires sudo, so we need to create a dummy sudo
echo -e "#!/bin/bash\nexec \"\$@\"" > /usr/bin/sudo
chmod +x /usr/bin/sudo

# Create a directory Rechunk expects
mkdir -p /home/runner/work/main/main
mkdir -p /home/runner/_work/main/main

- 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: Build Image
id: build-image
shell: bash
run: |
just check
just=$(which just)
$just build "${IMAGE_NAME}" "${DEFAULT_TAG}"

- name: Maximize build space
uses: ublue-os/remove-unwanted-software@517622d6452028f266b7ba4cc9a123b5f58a6b53 # v7
- name: Run Rechunker
# if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
id: rechunk
uses: hhd-dev/[email protected]
with:
remove-codeql: true

- name: Get current date
rechunk: "ghcr.io/hhd-dev/rechunk:v1.1.0"
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 Image
# if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
id: load
run: |
IMAGE=$(podman pull ${{ steps.rechunk.outputs.ref }})
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
# if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
env:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
run: |
echo ${{ secrets.GITHUB_TOKEN }} | podman login -u ${{ github.actor }} --password-stdin $registry
mkdir -p ~/.docker
cat /run/containers/0/auth.json > ~/.docker/config.json

# Push the image to GHCR (Image Registry)
- name: Push to GHCR
# if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
id: push
env:
IMAGE_REGISTRY: ${{ env.IMAGE_REGISTRY }}
IMAGE_NAME: ${{ env.IMAGE_NAME }}
IMAGE_DIGEST: ${{ steps.load.outputs.digest }}
PLATFORM: ${{ matrix.platform }}
run: |
podman tag ${{ env.IMAGE_REGISTRY }}/${IMAGE_NAME}:${DEFAULT_TAG} $IMAGE_REGISTRY/$IMAGE_NAME:$DEFAULT_TAG-$PLATFORM
for i in {1..3}; do
podman push --digestfile=/tmp/digestfile $IMAGE_REGISTRY/$IMAGE_NAME:$DEFAULT_TAG-$PLATFORM && break || sleep $((5 * i));
done
REMOTE_IMAGE_DIGEST=$(cat /tmp/digestfile)
echo "remote_image_digest=$REMOTE_IMAGE_DIGEST" >> $GITHUB_OUTPUT
cat /tmp/digestfile

# 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 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
# if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
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
# if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
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
# if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
needs:
- build_push
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Get Build Date
id: date
run: |
# Should generate a timestamp like what is defined on the ArtifactHub documentation
Expand Down Expand Up @@ -93,68 +237,65 @@ jobs:
sep-tags: " "
sep-annotations: " "

- name: Build Image
id: build-image
shell: bash
- name: Fetch Build Outputs
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
with:
pattern: ${{ env.IMAGE_NAME }}-*
merge-multiple: true
path: /tmp/artifacts

- name: Load Outputs
id: load-outputs
run: |
just=$(which just)
sudo $just build "${IMAGE_NAME}" "${DEFAULT_TAG}"
DIGESTS_JSON=$(jq -n '{}')
for digest_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

# 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 }}
labels: ${{ steps.metadata.outputs.labels }} # Rechunk strips out all the labels during build, this needs to be reapplied here with newline separator
- 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: Load in podman and tag
- name: Populate Manifest
env:
MANIFEST: ${{ steps.create-manifest.outputs.MANIFEST }}
DIGESTS_JSON: ${{ steps.load-outputs.outputs.DIGESTS_JSON }}
LABELS: ${{ steps.metadata.outputs.labels }}
run: |
IMAGE=$(podman pull ${{ steps.rechunk.outputs.ref }})
sudo rm -rf ${{ steps.rechunk.outputs.output }}
for tag in ${{ steps.metadata.outputs.tags }}; do
podman tag $IMAGE ${{ env.IMAGE_NAME }}:$tag
DIGESTS=$(echo "$DIGESTS_JSON" | jq -c '.')
PLATFORMS=(${{ env.PLATFORMS }})
for platform in ${PLATFORMS[@]}; do
digest=$(echo $DIGESTS | jq -r ".$platform")
echo "Adding ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}@$digest for $platform"
podman manifest add $MANIFEST ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}@$digest --arch $platform
done

- 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)
# Apply the labels to the manifest
for label in $(echo $LABELS | tr ' ' '\n'); do
podman manifest annotate --index --annotation=$label $MANIFEST
done

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Push the image to GHCR (Image Registry)
- name: Push To GHCR
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
with:
registry: ${{ env.IMAGE_REGISTRY }}
image: ${{ env.IMAGE_NAME }}
tags: ${{ steps.metadata.outputs.tags }}

# 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)
- name: Push Manifest
env:
MANIFEST: ${{ steps.create-manifest.outputs.MANIFEST }}
TAGS: ${{ steps.metadata.outputs.tags }}
IMAGE_REGISTRY: ${{ env.IMAGE_REGISTRY }}
IMAGE_NAME: ${{ env.IMAGE_NAME }}
run: |
IMAGE_FULL="${{ env.IMAGE_REGISTRY }}/${IMAGE_NAME}"
for tag in ${{ steps.metadata.outputs.tags }}; do
cosign sign -y --key env://COSIGN_PRIVATE_KEY $IMAGE_FULL:$tag
for tag in $(echo $TAGS | tr ' ' '\n'); do
podman manifest push --all=false $MANIFEST $IMAGE_REGISTRY/$IMAGE_NAME:$tag
done
env:
TAGS: ${{ steps.push.outputs.digest }}
COSIGN_EXPERIMENTAL: false
COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }}
6 changes: 2 additions & 4 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,5 @@ COPY build.sh /tmp/build.sh
RUN --mount=type=bind,from=config,src=/rpms,dst=/tmp/rpms chmod +x /tmp/build.sh && \
/tmp/build.sh && \
dnf clean all && \
ostree container commit

# Just gotta get this green!
RUN bootc container lint
ostree container commit && \
bootc container lint
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ build $target_image=image_name $tag=default_tag:
podman build \
"${BUILD_ARGS[@]}" \
--pull=newer \
--tag "${image_name}:${tag}" \
--tag "${target_image}:${tag}" \
.

_rootful_load_image $target_image=image_name $tag=default_tag:
Expand Down
Loading