Build amd64 and arm64 #564
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: Build amd64 and arm64 | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| RELEASE: | |
| description: 'AlmaLinux release' | |
| required: true | |
| default: '9' | |
| type: choice | |
| options: | |
| - '10-kitten' | |
| - '10' | |
| - '9' | |
| - 'ALL' | |
| schedule: | |
| # run every day at 03:00 UTC | |
| - cron: '00 03 * * *' | |
| # REQUIRED: Set up permissions for OIDC token and KMS access | |
| permissions: | |
| id-token: write | |
| contents: read | |
| packages: write | |
| env: | |
| LATEST_MAJOR: 9 | |
| IMAGE_NAME: almalinux-bootc | |
| VERSIONS_LIST: '"9", "10-kitten", "10"' | |
| AWS_REGION: us-east-1 | |
| AWS_ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/bootcimages | |
| KMS_KEY_ALIAS: cloudsig | |
| CHAT_CHANNEL: ${{ vars.MATTERMOST_CHANNEL }} | |
| jobs: | |
| set-versions-matrix: | |
| name: Set versions matrix | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| matrix: ${{ steps.set-matrix.outputs.matrix }} | |
| date_stamp: ${{ steps.date-stamp.outputs.date_stamp }} | |
| steps: | |
| - name: Set matrix | |
| id: set-matrix | |
| run: | | |
| # Format json for versions matrix | |
| case ${{ github.event_name }} in | |
| workflow_dispatch) | |
| if [[ "${{ inputs.RELEASE }}" == "ALL" ]]; then | |
| echo "matrix=$(jq -c <<< '[${{ env.VERSIONS_LIST }}]')" >> $GITHUB_OUTPUT | |
| else | |
| echo "matrix=$(jq -c <<< '["${{ inputs.RELEASE }}"]')" >> $GITHUB_OUTPUT | |
| fi | |
| ;; | |
| schedule) | |
| echo "matrix=$(jq -c <<< '[${{ env.VERSIONS_LIST }}]')" >> $GITHUB_OUTPUT | |
| ;; | |
| esac | |
| - name: Date stamp | |
| id: date-stamp | |
| run: | | |
| # date stamp | |
| date_stamp=$(date -u '+%Y%m%d') | |
| [ "x${date_stamp}" != "x" ] && echo "date_stamp=${date_stamp}" >> "$GITHUB_OUTPUT" | |
| build: | |
| name: Build image | |
| runs-on: ${{ matrix.ARCH == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} | |
| needs: [set-versions-matrix] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| VERSION_MAJOR: ${{ fromJSON(needs.set-versions-matrix.outputs.matrix) }} | |
| ARCH: [amd64, arm64, amd64/v2] | |
| exclude: | |
| - VERSION_MAJOR: 9 | |
| ARCH: amd64/v2 | |
| env: | |
| PLATFORM: linux/${{ matrix.ARCH }} | |
| DATE_STAMP: ${{ needs.set-versions-matrix.outputs.date_stamp }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - uses: ./.github/actions/shared-steps | |
| name: Build and Push | |
| with: | |
| VERSION_MAJOR: ${{ matrix.VERSION_MAJOR }} | |
| DATE_STAMP: ${{ env.DATE_STAMP }} | |
| IMAGE_REGISTRY: ${{ secrets.IMAGE_REGISTRY }} | |
| REGISTRY_USER: ${{ secrets.REGISTRY_USER }} | |
| REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} | |
| KMS_KEY_ALIAS: ${{ env.KMS_KEY_ALIAS }} | |
| AWS_ROLE_ARN: ${{ env.AWS_ROLE_ARN }} | |
| AWS_REGION: ${{ env.AWS_REGION }} | |
| push-manifest: | |
| if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} | |
| name: Push manifest | |
| needs: [set-versions-matrix, build] | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| VERSION_MAJOR: ${{ fromJSON(needs.set-versions-matrix.outputs.matrix) }} | |
| env: | |
| DATE_STAMP: ${{ needs.set-versions-matrix.outputs.date_stamp }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Fetch Build Outputs | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| pattern: ${{ env.IMAGE_NAME }}_${{ matrix.VERSION_MAJOR }}_* | |
| merge-multiple: true | |
| path: /tmp/artifacts | |
| - name: Load Outputs | |
| id: load-outputs | |
| run: | | |
| if [[ ! -d /tmp/artifacts ]]; then | |
| echo "No artifacts found, skipping manifest creation" | |
| echo "skip=1" >> "$GITHUB_ENV" | |
| exit | |
| fi | |
| ls -lR /tmp/artifacts/ | |
| jq -sc add /tmp/artifacts/*.json > merged.json | |
| echo "DIGESTS_JSON=$(cat merged.json)" >> $GITHUB_OUTPUT | |
| jq '.' merged.json | |
| - name: Sanity check | |
| if: ${{ env.skip != 1 }} | |
| id: check | |
| env: | |
| DIGESTS_JSON: ${{ steps.load-outputs.outputs.DIGESTS_JSON }} | |
| run: | | |
| [[ "${{ matrix.VERSION_MAJOR }}" == "9" ]] && EXPECTED=2 || EXPECTED=3 | |
| # Ensure we have the correct number of digests | |
| if [[ $(echo ${DIGESTS_JSON} | jq 'length') -ne $EXPECTED ]]; then | |
| echo "Expected $EXPECTED digests, found $(echo ${DIGESTS_JSON} | jq 'length')" | |
| exit 1 | |
| fi | |
| # Ensure all versions match | |
| COUNT=$(echo ${DIGESTS_JSON} | jq '[.[] | .version] | unique | length') | |
| if [[ $COUNT -ne 1 ]]; then | |
| echo "Versions do not match across all platforms" | |
| echo ${DIGESTS_JSON} | jq '[.[] | .version]' | |
| exit 1 | |
| fi | |
| echo "id=$(echo ${DIGESTS_JSON} | jq -r '[.[] | .id] | unique | .[0]')" >> $GITHUB_OUTPUT | |
| echo "version-id=$(echo ${DIGESTS_JSON} | jq -r '[.[] | .version] | unique | .[0]')" >> $GITHUB_OUTPUT | |
| echo "long-version=$(echo ${DIGESTS_JSON} | jq -r '[.[] | .long_version] | unique | .[0]')" >> $GITHUB_OUTPUT | |
| echo "vendor=$(echo ${DIGESTS_JSON} | jq -r '[.[] | .vendor] | unique | .[0]')" >> $GITHUB_OUTPUT | |
| echo "date=$(date -u +%Y\-%m\-%d\T%H\:%M\:%S\Z)" >> $GITHUB_OUTPUT | |
| - name: Login to registry (docker) | |
| if: ${{ env.skip != 1 }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ secrets.IMAGE_REGISTRY }} | |
| username: ${{ secrets.REGISTRY_USER }} | |
| password: ${{ secrets.REGISTRY_PASSWORD }} | |
| - name: Image Metadata | |
| if: ${{ env.skip != 1 }} | |
| uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 | |
| id: metadata | |
| with: | |
| tags: | | |
| type=raw,value=latest,enable=${{ matrix.VERSION_MAJOR == env.LATEST_MAJOR }} | |
| type=raw,value=${{ matrix.VERSION_MAJOR }} | |
| type=raw,value=${{ steps.check.outputs.version-id }},enable=${{ matrix.VERSION_MAJOR != '10-kitten' }} | |
| type=raw,value=${{ steps.check.outputs.version-id }}-${{ env.DATE_STAMP }} | |
| type=raw,value=${{ steps.check.outputs.long-version }} | |
| labels: | | |
| redhat.id=${{ steps.check.outputs.id }} | |
| redhat.version-id=${{ steps.check.outputs.version-id }} | |
| version=${{ steps.check.outputs.version-id }} | |
| release=${{ steps.check.outputs.version-id }} | |
| org.opencontainers.image.created=${{ steps.check.outputs.date }} | |
| org.opencontainers.image.source=${{ github.repositoryUrl }} | |
| org.opencontainers.image.title=${{ env.IMAGE_NAME }} | |
| org.opencontainers.image.url=${{ github.event.repository.html_url }} | |
| org.opencontainers.image.version=${{ steps.check.outputs.long-version }} | |
| org.opencontainers.image.vendor=AlmaLinux OS Foundation | |
| org.opencontainers.image.description=AlmaLinux Bootable Container Image | |
| - name: Get a newer podman (from debian trixie) | |
| if: ${{ env.skip != 1 }} | |
| run: | | |
| set -eux | |
| echo 'deb [trusted=yes] https://ftp.debian.org/debian/ trixie main' | sudo tee /etc/apt/sources.list.d/trixie.list | |
| sudo apt update | |
| sudo apt install -y crun/trixie podman/trixie | |
| - name: Create Manifest | |
| if: ${{ env.skip != 1 }} | |
| id: create-manifest | |
| run: | | |
| IMAGE_DEST=${{ secrets.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} | |
| podman manifest create ${IMAGE_DEST} | |
| echo "MANIFEST=${IMAGE_DEST}" >> $GITHUB_OUTPUT | |
| - name: Populate Manifest | |
| if: ${{ env.skip != 1 }} | |
| env: | |
| MANIFEST: ${{ steps.create-manifest.outputs.MANIFEST }} | |
| DIGESTS_JSON: ${{ steps.load-outputs.outputs.DIGESTS_JSON }} | |
| LABELS: ${{ steps.metadata.outputs.labels }} | |
| run: | | |
| IMAGE_DEST=${{ secrets.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} | |
| for platform in $(echo $DIGESTS_JSON | jq -r '. | to_entries | .[].key'); do | |
| digest=$(echo $DIGESTS_JSON | jq -r ".[\"$platform\"].digest") | |
| echo "Adding ${IMAGE_DEST}@$digest for $platform" | |
| if [[ "$platform" = */v* ]]; then | |
| podman manifest add $MANIFEST ${IMAGE_DEST}@$digest --arch ${platform%/*} --variant ${platform#*/} | |
| else | |
| podman manifest add $MANIFEST ${IMAGE_DEST}@$digest --arch $platform | |
| fi | |
| done | |
| # Apply the labels to the manifest (separated by newlines) | |
| while IFS= read -r label; do | |
| echo "Applying label $label to manifest" | |
| podman manifest annotate --index --annotation "$label" $MANIFEST | |
| done <<< "$LABELS" | |
| - name: Push Manifest | |
| if: ${{ env.skip != 1 }} | |
| id: push_manifest | |
| env: | |
| MANIFEST: ${{ steps.create-manifest.outputs.MANIFEST }} | |
| TAGS: ${{ steps.metadata.outputs.tags }} | |
| run: | | |
| IMAGE_DEST=${{ secrets.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} | |
| while IFS= read -r tag; do | |
| for i in {1..5}; do | |
| podman manifest push --all=false --digestfile=/tmp/digestfile $MANIFEST ${IMAGE_DEST}:$tag && break || sleep $((10*i)) | |
| done | |
| [ -f /tmp/digestfile ] || exit 1 | |
| done <<< "$TAGS" | |
| DIGEST=$(cat /tmp/digestfile) | |
| echo "digest=$DIGEST" >> $GITHUB_OUTPUT | |
| echo "image=${IMAGE_DEST}" >> $GITHUB_OUTPUT | |
| echo "Pushed manifest to ${IMAGE_DEST} with digest $DIGEST" | |
| podman manifest inspect ${IMAGE_DEST} | |
| - name: Sign Manifest | |
| if: ${{ env.skip != 1 }} | |
| uses: ./.github/actions/sign | |
| with: | |
| image-ref: ${{ steps.push_manifest.outputs.image }}@${{ steps.push_manifest.outputs.digest }} | |
| KMS_KEY_ALIAS: ${{ env.KMS_KEY_ALIAS }} | |
| AWS_ROLE_ARN: ${{ env.AWS_ROLE_ARN }} | |
| AWS_REGION: ${{ env.AWS_REGION }} | |
| - name: Format output for notification | |
| if: ${{ env.skip != 1 }} | |
| id: format | |
| env: | |
| DIGESTS_JSON: ${{ steps.load-outputs.outputs.DIGESTS_JSON }} | |
| TAGS: ${{ steps.metadata.outputs.tags }} | |
| run: | | |
| echo "arches=$(echo "$DIGESTS_JSON" | jq -r '[keys[] | "linux/" + .] | sort | join(", ")')" >> $GITHUB_OUTPUT | |
| # Add two spaces before each tag (starting from the second line) so that it is formatted correctly in the notification | |
| { | |
| echo 'tags<<EOF' | |
| printf "$TAGS" | awk 'NR==1{print; next} {print " " $0}' | |
| echo EOF | |
| } >> $GITHUB_OUTPUT | |
| - name: Notify AlmaLinux Chat | |
| if: ${{ env.skip != 1 }} | |
| uses: mattermost/action-mattermost-notify@master | |
| with: | |
| MATTERMOST_WEBHOOK_URL: ${{ secrets.MM_WEBHOOK_URL }} | |
| MATTERMOST_CHANNEL: ${{ env.CHAT_CHANNEL }} | |
| MATTERMOST_USERNAME: 'github' | |
| TEXT: |- | |
| **AlmaLinux OS ${{ steps.check.outputs.version-id }}** Bootc Image, build `${{ steps.check.outputs.long-version }}`, built by [GitHub Actions](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | |
| :almalinux: **${{ steps.push_manifest.outputs.image }}** | |
| - Platforms: | |
| ``` | |
| ${{ steps.format.outputs.arches }} | |
| ``` | |
| - Tags: | |
| ``` | |
| ${{ steps.format.outputs.tags }} | |
| ``` |