name: Build, test and push to the Client Library on: workflow_dispatch: inputs: production: description: | 'Push to production registries' 'not checked - to testing' required: true type: boolean default: false version_major: description: 'AlmaLinux major version' required: true default: '9' type: choice options: - 9 - 8 type_default: description: 'default' required: true type: boolean default: true type_minimal: description: 'minimal' required: true type: boolean default: true type_micro: description: 'micro' required: true type: boolean default: true type_base: description: 'base' required: true type: boolean default: true type_init: description: 'init' required: true type: boolean default: true env: # Latest version version_latest: 9 # Platforms list: linux/amd64, linux/ppc64le, linux/s390x, linux/arm64 platforms: 'linux/amd64, linux/ppc64le, linux/s390x, linux/arm64' # Registries list: # for production: docker.io/almalinux, quay.io/almalinuxorg, ghcr.io/almalinux # for testing: quay.io/almalinuxautobot registries: ${{ inputs.production && 'docker.io/almalinux, quay.io/almalinuxorg, ghcr.io/almalinux' || 'quay.io/almalinuxautobot, ghcr.io/almalinux' }} jobs: build-test-push: name: Deploy ${{ inputs.version_major }} ${{ matrix.image_types }} images runs-on: ubuntu-latest strategy: fail-fast: false matrix: # Set image types matrix based on boolean inputs.type_* with true value image_types: ${{ fromJSON(format('["{0}", "{1}", "{2}", "{3}", "{4}"]', ( inputs.type_default && 'default' ), ( inputs.type_minimal && 'minimal' ), ( inputs.type_micro && 'micro' ), ( inputs.type_base && 'base' ), ( inputs.type_init && 'init' ) )) }} exclude: - image_types: 'false' steps: - name: Prepare AlmaLinux Minor version number run: | case ${{ inputs.version_major }} in 8) version_minor="9" ;; 9) version_minor="3" ;; 10) version_minor="0" ;; *) echo "Almalinux ${{ inputs.version_major }} is not supported!" && false esac echo "version_minor=${version_minor}" >> $GITHUB_ENV # [Debug] echo "version_minor=${version_minor}" - name: Prepare date stamp id: date_stamp run: | # date stamp date_stamp=$(date -u '+%Y%m%d') echo "date_stamp=${date_stamp}" >> $GITHUB_ENV echo "date_stamp=${date_stamp}" >> "$GITHUB_OUTPUT" [ -z "$date_stamp-x" ] && false # [Debug] echo "date_stamp=${date_stamp}" - name: Generate list of images to use as base name for tags run: | # list of registries to push to REGISTRIES="${{ env.registries }}" IMAGE_NAMES= # generate image names in format $REGISTRY/almalinux or $REGISTRY/${{ inputs.version_major }}-${{ matrix.image_types }} # image names are used by docker/metadata-action to set 'images' for REGISTRY in ${REGISTRIES//,/ }; do # 'default' images should not go to docker.io [ "${{ matrix.image_types }}" = "default" ] && [[ $REGISTRY = *'docker.io'* ]] && continue # 'default' images goes to $REGISTRY/almalinux [ "${{ matrix.image_types }}" = "default" ] \ && IMAGE_NAME="$REGISTRY/almalinux" \ || IMAGE_NAME="$REGISTRY/${{ inputs.version_major }}-${{ matrix.image_types }}" IMAGE_NAMES="${IMAGE_NAMES} ${IMAGE_NAME}" unset IMAGE_NAME done # remove space at the beginning of string IMAGE_NAMES=${IMAGE_NAMES# } # separate with comma instead of space and export to the action echo "IMAGE_NAMES=${IMAGE_NAMES// /,}" >> $GITHUB_ENV # [Debug] echo $IMAGE_NAMES - name: Enable containerd image store on Docker Engine run: | # Use containerd image store sudo jq '.features |= . + { "containerd-snapshotter": true }' /etc/docker/daemon.json > ./daemon.json.${{ env.date_stamp }} && \ sudo mv -f ./daemon.json.${{ env.date_stamp }} /etc/docker/daemon.json sudo systemctl restart docker docker info -f '{{ .DriverStatus }}' - name: Checkout ${{ github.repository }}, branch 'main' uses: actions/checkout@v4 - name: Checkout ${{ github.repository }}, branch '${{ inputs.version_major }}', path '${{ inputs.version_major }}' uses: actions/checkout@v4 with: ref: ${{ inputs.version_major }} path: ${{ inputs.version_major }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker.io if: contains(env.registries, 'docker.io') uses: docker/login-action@v3 with: registry: docker.io username: ${{ inputs.production && secrets.DOCKERHUB_USERNAME || secrets.TEST_DOCKERHUB_USERNAME }} password: ${{ inputs.production && secrets.DOCKERHUB_TOKEN || secrets.TEST_DOCKERHUB_TOKEN }} - name: Login to Quay.io if: contains(env.registries, 'quay.io') uses: docker/login-action@v3 with: registry: quay.io username: ${{ inputs.production && secrets.QUAY_IO_USERNAME || secrets.TEST_QUAY_IO_USERNAME }} password: ${{ inputs.production && secrets.QUAY_IO_CLI_PASSWORD || secrets.TEST_QUAY_IO_CLI_PASSWORD }} - name: Login to Ghcr.io if: contains(env.registries, 'ghcr.io') uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ inputs.production && secrets.GIT_HUB_USERNAME || secrets.TEST_GITHUB_USERNAME }} password: ${{ inputs.production && secrets.GIT_HUB_TOKEN || secrets.TEST_GITHUB_TOKEN }} - name: Generate tags and prepare metadata to build and push id: meta uses: docker/metadata-action@v5 with: # list of Docker images to use as base names for tags images: ${{ env.IMAGE_NAMES }} # list of tags tags: | type=raw,value=latest,enable=${{ matrix.image_types != 'default' || ( matrix.image_types == 'default' && inputs.version_major == env.version_latest ) }} type=raw,value=${{ inputs.version_major }},enable=true type=raw,value=${{ inputs.version_major }}.${{ env.version_minor }},enable=true type=raw,value=${{ inputs.version_major }}.${{ env.version_minor }}-${{ env.date_stamp }},enable=true - name: Build images id: build-images uses: docker/build-push-action@v5 with: provenance: false context: "{{defaultContext}}:Containerfiles/${{ inputs.version_major }}" file: ./Containerfile.${{ matrix.image_types }} platforms: ${{ env.platforms }} push: false load: true tags: ${{ steps.meta.outputs.tags }} - name: Test images id: test-images run: | # [Test] platforms="${{ env.platforms }}" for platform in ${platforms//,/ }; do echo "Testing AlmaLinux ${{ inputs.version_major }} ${{ matrix.image_types }} for ${platform} image:" docker run --platform=${platform} ${{ steps.build-images.outputs.digest }} /bin/bash -c " \ uname -m \ && cat /etc/almalinux-release \ && ( test "${{ matrix.image_types }}" != "micro" && rpm -q gpg-pubkey) || true " done - name: Push images to Client Library id: push-images uses: docker/build-push-action@v5 with: provenance: false context: "{{defaultContext}}:Containerfiles/${{ inputs.version_major }}" file: ./Containerfile.${{ matrix.image_types }} platforms: ${{ env.platforms }} push: true tags: ${{ steps.meta.outputs.tags }} - name: Extract RootFS (default and minimal only) id: extract-rootfs # 'default' or 'minimal' images only go to Docker Official Library if: matrix.image_types == 'default' || matrix.image_types == 'minimal' run: | # [RootFS] # File name for RootFS file (packed with tag + Xz) name=almalinux-${{ inputs.version_major }}-${{ matrix.image_types }} pwd=$( pwd ) path=${pwd}/${name} # The "tar file" for 'docker save' to write to tar_name=${pwd}/${name}.tar mkdir ${path} cd ${path} # Produce a tarred repository and save it to the "tar file". docker save ${{ steps.build-images.outputs.digest }} -o ${tar_name} # Extract the "tar file" tar xf ${tar_name} cd blobs/sha256 # The "temporary Dockerfile" to build image based on RootFS cat < Dockerfile FROM scratch ADD rootfs.tar.gz / CMD ["/bin/bash"] EOF # Loop blobs to find all zipped files that are RootFS for a particular architecture for file in `find . -type f`; do if file --brief ${file} | grep -i gzip >/dev/null; then # Make a copy of "taken RootFS" cp -av ${file} rootfs.tar.gz # Build an image from the "temporary Dockerfile" docker build -t rootfs . # Run the image and query almalinux-release package's architecture arch=$( docker run --rm rootfs /bin/bash -c "rpm -q --qf=%{ARCH} almalinux-release" ) # Map found architecture to the corresponding platform platform= docker rmi rootfs case ${arch} in x86_64) platform=amd64;; ppc64le) platform=ppc64le;; s390x) platform=s390x;; aarch64) platform=arm64;; *) echo "The '$arch' is incorrect or failed to determine architecture." && false;; esac # Delete copy of the "taken RootFS" rm -f rootfs.tar.gz # Copy the "taken RootFS" into corresponded .tar.xz cp -av ${file} ${name}-${platform}.tar.gz zcat ${name}-${platform}.tar.gz | xz -9 -e -T0 > ${pwd}/${{ inputs.version_major }}/${{ matrix.image_types }}/${platform}/${name}-${platform}.tar.xz fi done # Clean up rm -rf ${path} echo "[Debug]" ls -1 ${pwd}/${{ inputs.version_major }}/${{ matrix.image_types }}/*/*.tar.xz # Change date stamp in '${version_major}/${image_types}/${arch}/Dockerfile' - name: Change date stamp in Dockerfile (default and minimal only) # 'default' or 'minimal' images only go to Docker Official Library if: matrix.image_types == 'default' || matrix.image_types == 'minimal' run: | # [Dockerfile] platforms="${{ env.platforms }}" for platform in ${platforms//,/ }; do arch=${platform#linux/} dockerfile=${{ inputs.version_major }}/${{ matrix.image_types }}/${arch}/Dockerfile case ${{ matrix.image_types }} in default) tags="${{ inputs.version_major }}, ${{ inputs.version_major }}.${{ env.version_minor }}, ${{ inputs.version_major }}.${{ env.version_minor }}-${{ env.date_stamp }}" [ "${{ inputs.version_major }}" = "9" ] && tags="latest, ${tags}" ;; minimal) tags="${{ inputs.version_major }}-${{ matrix.image_types }}, ${{ inputs.version_major }}.${{ env.version_minor }}-${{ matrix.image_types }}, ${{ inputs.version_major }}.${{ env.version_minor }}-${{ matrix.image_types }}-${{ env.date_stamp }}" [ "${{ inputs.version_major }}" = "9" ] && tags="minimal, ${tags}" ;; *) esac # Tags: 8, 8.9, 8.9-20231124 sed -i "/^\([[:space:]]*#[[:space:]]*Tags: \).*/s//\1${tags}/" ${dockerfile} echo "[Debug] ${dockerfile}" cat ${dockerfile} done - name: "Prepare time stamp" id: time_stamp run: | # time stamp time_stamp=$(date -u '+%H:%M:%S') echo "time_stamp=${time_stamp}" >> $GITHUB_ENV echo "time_stamp=${time_stamp}" >> "$GITHUB_OUTPUT" [ -z "$time_stamp-x" ] && false # [Debug] echo "time_stamp=${time_stamp}" # Commit '${version_major}/${image_types}/${arch}/*' - name: "Commit and push ${{ matrix.image_types }}/*/* Dockerfile and RootFS (branch ${{ inputs.version_major }})" # 'default' or 'minimal' images only and 'Push to production' is checked if: ( matrix.image_types == 'default' || matrix.image_types == 'minimal' ) && inputs.production uses: EndBug/add-and-commit@v9 with: default_author: user_info new_branch: ${{ inputs.version_major }} cwd: ${{ inputs.version_major }} pull: '--rebase --autostash' message: "AlmaLinux ${{ inputs.version_major }} ${{ matrix.image_types }} - ${{ env.date_stamp }} ${{ env.time_stamp }} (generated on ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})." push: true outputs: date_stamp: ${{ steps.date_stamp.outputs.date_stamp }} time_stamp: ${{ steps.time_stamp.outputs.time_stamp }} optimize-repo-size: # 'default' or 'minimal' images only and 'Push to production' is checked if: ( inputs.type_default || inputs.type_minimal ) && inputs.production name: Optimize ${{ github.repository }} size of the branch '${{ inputs.version_major }}' runs-on: ubuntu-latest needs: - build-test-push steps: - name: Checkout ${{ github.repository }}, branch '${{ inputs.version_major }}', path '${{ inputs.version_major }}' uses: actions/checkout@v4 with: ref: ${{ inputs.version_major }} path: ${{ inputs.version_major }} - name: Optimize size of branch the '${{ inputs.version_major }}' run: | date_stamp=${{ needs.build-test-push.outputs.date_stamp }} cd ${{ inputs.version_major }} echo "Prepare new branch 'tmp' based on ${{ inputs.version_major }}" git checkout -b tmp echo "Delete local branch '${{ inputs.version_major }}'" git branch -D ${{ inputs.version_major }} echo "Preserve resent data" mkdir ../tmp-${date_stamp} mv ./default ../tmp-${date_stamp}/ mv ./minimal ../tmp-${date_stamp}/ echo "Crete orphan branch '${{ inputs.version_major }}'" git checkout --orphan ${{ inputs.version_major }} echo "Clean up" git rm --cached -r . rm -rf ./default rm -rf ./minimal echo "Restore resent data" mv ../tmp-${date_stamp}/default ./ mv ../tmp-${date_stamp}/minimal ./ echo "[Debug]" git status - name: Commit and push ${{ github.repository }}, branch '${{ inputs.version_major }}' uses: EndBug/add-and-commit@v9 with: default_author: user_info message: "Update AlmaLinux ${{ inputs.version_major }} - ${{ needs.build-test-push.outputs.date_stamp }} ${{ needs.build-test-push.outputs.time_stamp }} (generated on ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})." push: '--force --set-upstream origin ${{ inputs.version_major }}' cwd: ${{ inputs.version_major }}