diff --git a/.github/.syncignore b/.github/.syncignore index 3d8f02c..c03a7a5 100644 --- a/.github/.syncignore +++ b/.github/.syncignore @@ -1,2 +1,3 @@ CODEOWNERS -workflows/create-release.yml \ No newline at end of file +workflows/create-release.yml +workflows/push-image.yml \ No newline at end of file diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index f779345..074f6d3 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -2,10 +2,10 @@ name: Create Release on: schedule: - - cron: "*/30 * * * *" # every 30 minutes + - cron: '*/30 * * * *' # every 30 minutes push: branches: - - main + - main workflow_dispatch: inputs: version: @@ -22,55 +22,120 @@ on: concurrency: release env: - STACKS_FILENAME: "stacks/images.json" - + STACKS_FILEPATH: "stacks/images.json" + PATCHED_USNS_FILENAME: "patched-usns.json" jobs: preparation: name: Preparation runs-on: ubuntu-22.04 outputs: - stacks: ${{ steps.get-stack-names.outputs.stacks }} - support_usns: ${{ steps.get-stack-names.outputs.support_usns }} + stacks_added: ${{ steps.get-stacks.outputs.stacks_added }} + stacks: ${{ steps.get-stacks.outputs.stacks }} + support_usns: ${{ steps.polling-os-type.outputs.support_usns }} + architectures: ${{ steps.lookup.outputs.platforms }} + archs_added: ${{ steps.lookup.outputs.platforms_added }} + polling_type: ${{ steps.polling-os-type.outputs.polling_type }} github_repo_name: ${{ steps.repo.outputs.github_repo_name }} registry_repo_name: ${{ steps.repo.outputs.registry_repo_name }} repo_owner: ${{ steps.repo.outputs.repo_owner }} + default_stack_dir: ${{ steps.lookup.outputs.default_stack_dir }} + stack_files_dir: ${{ steps.get-stacks.outputs.stack_files_dir }} steps: - name: Checkout repo uses: actions/checkout@v4 + with: + fetch-depth: 0 # gets full history - - name: Get stack names and USN support - id: get-stack-names + - name: Get stacks images + id: get-stacks run: | - stacks=$( + + if [[ -f ${{ env.STACKS_FILEPATH }} ]]; then + current_stacks=$(jq '[.images[] | + . + + { + "create_build_image": (.create_build_image // false), + "is_new": true + }]' ${{ env.STACKS_FILEPATH }} ) + + stack_files_dir="$(dirname "${{ env.STACKS_FILEPATH }}")" + + git show $(git describe --tags --abbrev=0):"${{ env.STACKS_FILEPATH }}" > ./previous_images.json + + if [ -f "./previous_images.json" ]; then + previous_stacks=$(jq '[.images[] | + . + + { + "create_build_image": (.create_build_image // false), + "is_new": false + }]' "./previous_images.json" ) + else + previous_stacks="[]" + fi + + stacks=$(echo "$current_stacks" | jq -c --argjson prev "$previous_stacks" ' + map( + . as $curr | + ($prev[] | select(.name == $curr.name)) // $curr + ) + ') + else + stacks=$( cat <> "$GITHUB_OUTPUT" + + ## Filter stacks array to include the minimum number of attributes + stacks=$(echo "$stacks" | jq 'map({ + name, + config_dir, + output_dir, + build_image, + build_receipt_filename, + run_image, + run_receipt_filename, + create_build_image, + base_build_container_image, + base_run_container_image, + is_new + })') + + stacks=$(jq -c <<< "$stacks" ) + printf "stacks=%s\n" "${stacks}" >> "$GITHUB_OUTPUT" + + - name: Polling OS type + id: polling-os-type + run: | support_usns=true - if [[ -f ${{ env.STACKS_FILENAME }} ]]; then - stacks=$(jq '[.images[] | - . + - { - "create_build_image": (.create_build_image // false) - }]' ${{ env.STACKS_FILENAME }} ) + if [[ -f ${{ env.STACKS_FILEPATH }} ]]; then + support_usns=$( jq '.support_usns' ${{ env.STACKS_FILEPATH }} ) + fi - support_usns=$( jq '.support_usns' ${{ env.STACKS_FILENAME }} ) + if [ $support_usns == true ]; then + echo "polling_type=usn" >> "$GITHUB_OUTPUT" + else + echo "polling_type=hash" >> "$GITHUB_OUTPUT" fi - stacks=$(jq -c <<< "$stacks" ) - printf "stacks=%s\n" "${stacks}" >> "$GITHUB_OUTPUT" - printf "support_usns=%s\n" "${support_usns}" >> "$GITHUB_OUTPUT" + echo "support_usns=${support_usns}" >> "$GITHUB_OUTPUT" - name: Get Repository Name id: repo @@ -90,81 +155,308 @@ jobs: repo_owner="${GITHUB_REPOSITORY_OWNER/-/}" printf "repo_owner=%s\n" "${repo_owner}" >> "$GITHUB_OUTPUT" + - name: Lookup Supported Architectures + id: lookup + run: | + #! /usr/bin/env bash + + set -euo pipefail + shopt -s inherit_errexit + + # install yj to parse TOML + curl -L $(curl -sL https://api.github.com/repos/sclevine/yj/releases/latest | jq -r '.assets[] | select(.name=="yj-linux-amd64").browser_download_url') -o yj + chmod +x yj + + default_stack_dir=$(echo '${{ steps.get-stacks.outputs.stacks }}' | jq -r '.[] | select(.name=="default") | .config_dir') + echo "default_stack_dir=${default_stack_dir}" >> "$GITHUB_OUTPUT" + + current_platforms="$(cat "$default_stack_dir/stack.toml" | ./yj -tj | jq -c '[.platforms[] | { name: sub("linux/"; "") , is_new: true }]')" + + # get previous platforms + git show $(git describe --tags --abbrev=0):"$default_stack_dir/stack.toml" > ./previous_stack.toml + + previous_platforms="$(cat ./previous_stack.toml | ./yj -tj | jq -c '[.platforms[] | { name: sub("linux/"; "") , is_new: false }]')" + + platforms=$(echo "$current_platforms" | jq -c --argjson prev "$previous_platforms" ' + map( + . as $curr | + ($prev[] | select(.name == $curr.name)) // $curr + ) + ') + + echo "platforms=${platforms}" >> "$GITHUB_OUTPUT" + + # The following job is specific to Ubuntu images. It checks for new + # USNs (Ubuntu Security Notices) and triggers the flow to create + # a new release with the latest images that have the USNs patched. + poll_usns: + name: Poll USNs + runs-on: ubuntu-22.04 + needs: [preparation] + if: ${{ needs.preparation.outputs.polling_type == 'usn' }} + strategy: + matrix: + stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }} + arch: ${{ fromJSON(needs.preparation.outputs.architectures) }} + outputs: + usns: ${{ steps.new_usns.outputs.usns }} + steps: + - name: Generate receipt asset patterns + id: receipt_pattern + run: | + if [ "${{ matrix.arch.name }}" = "amd64" ]; then + if [ ${{ matrix.stacks.create_build_image }} == true ]; then + echo "build=${{ needs.preparation.outputs.github_repo_name }}-\\d+\\.\\d+(\\.\\d+)?-${{ matrix.stacks.build_receipt_filename }}" >> "$GITHUB_OUTPUT" + fi + echo "run=${{ needs.preparation.outputs.github_repo_name }}-\\d+\\.\\d+(\\.\\d+)?-${{ matrix.stacks.run_receipt_filename }}" >> "$GITHUB_OUTPUT" + else + if [ ${{ matrix.stacks.create_build_image }} == true ]; then + echo "build=${{ matrix.arch.name }}-${{ matrix.stacks.build_receipt_filename }}" >> "$GITHUB_OUTPUT" + fi + echo "run=${{ matrix.arch.name }}-${{ matrix.stacks.run_receipt_filename }}" >> "$GITHUB_OUTPUT" + fi + + - name: Write Empty Previous Receipts + if: ${{ matrix.arch.is_new == true || matrix.stacks.is_new == true }} + run: | + if [ ${{ matrix.stacks.create_build_image }} == true ]; then + if [ ! -f "./${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" ]; then + echo '{"components":[]}' > "./${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" + fi + fi + if [ ! -f "./${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" ]; then + echo '{"components":[]}' > "./${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" + fi + + - name: Check for Previous Releases + id: check_previous + run: | + gh auth status + # shellcheck disable=SC2046 + if [ $(gh api "/repos/${{ github.repository }}/releases" | jq -r 'length') -eq 0 ]; then + echo "exists=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "exists=true" >> "$GITHUB_OUTPUT" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Find and Download Previous Build Receipt + if: ${{ matrix.arch.is_new == false && matrix.stacks.is_new == false && steps.check_previous.outputs.exists == 'true' }} + id: previous_build + uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main + with: + asset_pattern: "${{ steps.receipt_pattern.outputs.build }}" + search_depth: 1 + repo: ${{ github.repository }} + output_path: "/github/workspace/${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" + token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} + strict: true + + - name: Find and Download Previous Run Receipt + if: ${{ matrix.arch.is_new == false && matrix.stacks.is_new == false && steps.check_previous.outputs.exists == 'true' }} + id: previous_run + uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main + with: + asset_pattern: "${{ steps.receipt_pattern.outputs.run }}" + search_depth: 1 + repo: ${{ github.repository }} + output_path: "/github/workspace/${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" + token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} + strict: true + + - name: Get Package List + id: packages + if: ${{ steps.previous_build.outputs.output_path != '' && steps.previous_run.outputs.output_path != '' }} + uses: paketo-buildpacks/github-config/actions/stack/generate-package-list@main + with: + build_receipt: "${{ github.workspace }}/${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" + run_receipt: "${{ github.workspace }}/${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" + + - name: Generate USNs download asset pattern + id: usn_download_pattern + run: | + arch_prefix="" + if [ "${{ matrix.arch.name }}" = "amd64" ]; then + arch_prefix="" + else + arch_prefix="${{ matrix.arch.name }}" + fi + + stack_name_prefix="" + if [ "${{ matrix.stacks.name }}" = "default" ]; then + stack_name_prefix="" + else + stack_name_prefix="${{ matrix.stacks.name }}" + fi + + pattern=$(echo '["\\d+.\\d+(.\\d+)?","'"$stack_name_prefix"'", "'"$arch_prefix"'", "${{ env.PATCHED_USNS_FILENAME }}"]' | jq -r 'map(select(length > 0)) | join("-")') + + echo "pattern=$pattern" >> "$GITHUB_OUTPUT" + + - name: Find and Download Previous Patched USNs + id: download_patched + uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main + with: + asset_pattern: "${{ steps.usn_download_pattern.outputs.pattern }}" + search_depth: "-1" # Search all releases + repo: ${{ github.repository }} + output_path: "/github/workspace/${{ matrix.arch.name }}-${{ matrix.stacks.name }}-${{ env.PATCHED_USNS_FILENAME }}-previous" + token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} + + - name: Output Patched USNs as JSON String + id: patched + if: ${{ steps.download_patched.outputs.output_path != '' }} + run: | + patched=$(jq --compact-output . < "${GITHUB_WORKSPACE}/${{ matrix.arch.name }}-${{ matrix.stacks.name }}-${{ env.PATCHED_USNS_FILENAME }}-previous") + printf "patched=%s\n" "${patched}" >> "$GITHUB_OUTPUT" + + - name: Get Stack Distribution Name + id: distro + run: | + # Extract distro from repo name: + # paketo-buildpacks/jammy-tiny-stack --> jammy + distro="$(echo "${{ github.repository }}" | sed 's/^.*\///' | sed 's/\-.*$//')" + echo "Ubuntu distribution: ${distro}" + printf "distro=%s\n" "${distro}" >> "$GITHUB_OUTPUT" + + - name: Get New USNs + id: usns + uses: paketo-buildpacks/github-config/actions/stack/get-usns@main + with: + distribution: ${{ steps.distro.outputs.distro }} + packages: ${{ steps.packages.outputs.packages }} + last_usns: ${{ steps.patched.outputs.patched }} + + - name: Write USNs File + id: write_usns + run: | + jq . <<< "${USNS}" > "./${USNS_PATH}" + echo "usns=./${USNS_PATH}" >> "$GITHUB_OUTPUT" + env: + USNS_PATH: "${{ matrix.arch.name }}-${{ matrix.stacks.name }}-${{ env.PATCHED_USNS_FILENAME }}" + USNS: ${{ steps.usns.outputs.usns }} + + - name: Upload USNs file + uses: actions/upload-artifact@v4 + with: + name: "${{ matrix.arch.name }}-${{ matrix.stacks.name }}-${{ env.PATCHED_USNS_FILENAME }}" + path: "${{ matrix.arch.name }}-${{ matrix.stacks.name }}-${{ env.PATCHED_USNS_FILENAME }}" + + - name: Are any new USNs + id: new_usns + run: | + new_usns_length=$(cat "${{ matrix.arch.name }}-${{ matrix.stacks.name }}-${{ env.PATCHED_USNS_FILENAME }}" | jq 'length') + + if [ "$new_usns_length" -ge 0 ]; then + echo "usns=true" >> "$GITHUB_OUTPUT" + fi + + # The job below checks if new images are available on the registry + # based on the sha256 checksum. If yes, it triggers the flow + # to create a new release with the latest images poll_images: - name: Poll Images + name: Poll Images based on the hash code runs-on: ubuntu-22.04 + if: ${{ needs.preparation.outputs.polling_type == 'hash' }} needs: preparation strategy: matrix: stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }} + arch: ${{ fromJSON(needs.preparation.outputs.architectures) }} outputs: images_need_update: ${{ steps.compare_previous_and_current_sha256_hash_codes.outputs.images_need_update }} steps: + - name: Generate hash code asset patterns + id: hashcode_pattern + run: | + if [ "${{ matrix.arch.name }}" = "amd64" ]; then + echo "build=${{ needs.preparation.outputs.github_repo_name }}-\\d+.\\d+(.\\d+)?-${{ matrix.stacks.build_image }}.oci.sha256" >> "$GITHUB_OUTPUT" + echo "run=${{ needs.preparation.outputs.github_repo_name }}-\\d+.\\d+(.\\d+)?-${{ matrix.stacks.run_image }}.oci.sha256" >> "$GITHUB_OUTPUT" + else + echo "build=-${{ matrix.arch.name }}-${{ matrix.stacks.build_image }}.oci.sha256" >> "$GITHUB_OUTPUT" + echo "run=-${{ matrix.arch.name }}-${{ matrix.stacks.run_image }}.oci.sha256" >> "$GITHUB_OUTPUT" + fi + - name: Find and Download Previous build image hash code of stack ${{ matrix.stacks.build_image }} if: ${{ matrix.stacks.create_build_image == true }} uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main with: - asset_pattern: "${{ matrix.stacks.build_image }}.oci.sha256" + asset_pattern: "${{ steps.hashcode_pattern.outputs.build }}" search_depth: 1 repo: ${{ github.repository }} - output_path: "/github/workspace/previous_${{ matrix.stacks.build_image }}.oci.sha256" + output_path: "./previous_${{ matrix.arch.name }}-${{ matrix.stacks.build_image }}.oci.sha256" token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} - name: Find and Download Previous run image hash code of stack ${{ matrix.stacks.run_image }} uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main with: - asset_pattern: "${{ matrix.stacks.run_image }}.oci.sha256" + asset_pattern: "${{ steps.hashcode_pattern.outputs.run }}" search_depth: 1 repo: ${{ github.repository }} - output_path: "/github/workspace/previous_${{ matrix.stacks.run_image }}.oci.sha256" + output_path: "./previous_${{ matrix.arch.name }}-${{ matrix.stacks.run_image }}.oci.sha256" token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} - - name: Get current run image hash code of ${{ matrix.stacks.name }} stack + - name: Get current build image hash code of ${{ matrix.stacks.name }} stack with arch ${{ matrix.arch.name }} + if: ${{ matrix.stacks.create_build_image == true }} run: | - skopeo inspect --format "{{.Digest}}" ${{ matrix.stacks.base_run_container_image }} > hash-code-current-run-image-${{ matrix.stacks.name }} + skopeo inspect --format "{{.Digest}}" ${{ matrix.stacks.base_build_container_image }} > ./hash-code-current-build-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }} - - name: Get current build image hash code of ${{ matrix.stacks.name }} stack - if: ${{ matrix.stacks.create_build_image == true }} + - name: Get current run image hash code of ${{ matrix.stacks.name }} stack with arch ${{ matrix.arch.name }} run: | - skopeo inspect --format "{{.Digest}}" ${{ matrix.stacks.base_build_container_image }} > hash-code-current-build-image-${{ matrix.stacks.name }} + skopeo inspect --format "{{.Digest}}" ${{ matrix.stacks.base_run_container_image }} > ./hash-code-current-run-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }} + + - name: Create empty image hash codes + run: | + if [ ! -f "./hash-code-current-build-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }}" ]; then + touch "./hash-code-current-build-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }}" + fi + if [ ! -f "./hash-code-current-run-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }}" ]; then + touch "./hash-code-current-run-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }}" + fi - name: Upload run image hash code uses: actions/upload-artifact@v4 with: - name: hash-code-current-run-image-${{ matrix.stacks.name }} - path: hash-code-current-run-image-${{ matrix.stacks.name }} + name: hash-code-current-run-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }} + path: hash-code-current-run-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }} if-no-files-found: error - name: Upload build image hash code if: ${{ matrix.stacks.create_build_image == true }} uses: actions/upload-artifact@v4 with: - name: hash-code-current-build-image-${{ matrix.stacks.name }} - path: hash-code-current-build-image-${{ matrix.stacks.name }} + name: hash-code-current-build-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }} + path: hash-code-current-build-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }} if-no-files-found: error - name: Compare previous and current hash codes id: compare_previous_and_current_sha256_hash_codes run: | - if [ "$(cat previous_${{ matrix.stacks.run_image }}.oci.sha256)" != "$(cat hash-code-current-run-image-${{ matrix.stacks.name }})" ]; then + if [ "$(cat previous_${{ matrix.arch.name }}-${{ matrix.stacks.run_image }}.oci.sha256)" != "$(cat hash-code-current-run-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }})" ]; then echo "images_need_update=true" >> "$GITHUB_OUTPUT" fi if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then - if [ "$(cat previous_${{ matrix.stacks.build_image }}.oci.sha256)" != "$(cat hash-code-current-build-image-${{ matrix.stacks.name }})" ]; then + if [ "$(cat previous_${{ matrix.arch.name }}-${{ matrix.stacks.build_image }}.oci.sha256)" != "$(cat hash-code-current-build-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }})" ]; then echo "images_need_update=true" >> "$GITHUB_OUTPUT" fi fi + # If there is no change on the usns, and there is no change on the image hash codes + # and the event is schedule, then there is no need to run below workflow as nothing has changed stack_files_changed: name: Determine If Stack Files Changed runs-on: ubuntu-22.04 - needs: [ poll_images, preparation ] - if: ${{ ! ( needs.poll_images.outputs.images_need_update == null && github.event_name == 'schedule' ) }} - strategy: - matrix: - stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }} + needs: [ preparation, poll_usns, poll_images ] + if: ${{ + !failure() && !cancelled() && + !( + (needs.poll_images.outputs.images_need_update == null && + needs.poll_usns.outputs.usns == null ) && + github.event_name == 'schedule' + ) }} outputs: stack_files_changed: ${{ steps.compare.outputs.stack_files_changed }} steps: @@ -177,10 +469,13 @@ jobs: id: compare run: | # shellcheck disable=SC2046 - changed="$(git diff --name-only $(git describe --tags --abbrev=0) -- ${{ matrix.stacks.config_dir }})" - if [ -n "${changed}" ] + changed=$(git diff --name-only $(git describe --tags --abbrev=0) -- "${{ needs.preparation.outputs.stack_files_dir }}") + if [ -z "${changed}" ] then - echo "Relevant files of ${{ matrix.stacks.name }}, have changed since previous release." + echo "No relevant files changed since previous release." + echo "stack_files_changed=false" >> "$GITHUB_OUTPUT" + else + echo "Relevant files have changed since previous release." echo "${changed}" echo "stack_files_changed=true" >> "$GITHUB_OUTPUT" fi @@ -189,16 +484,53 @@ jobs: name: Run If Stack Files Changed runs-on: ubuntu-22.04 needs: [stack_files_changed] - if: ${{ needs.stack_files_changed.outputs.stack_files_changed == 'true' }} + if: ${{ !failure() && !cancelled() && needs.stack_files_changed.outputs.stack_files_changed == 'true' }} steps: - name: Run if stack files changed run: | echo "stack files have changed" + usns_or_sha_changed: + name: USNs or SHAs have changed + runs-on: ubuntu-22.04 + outputs: + changed: ${{ steps.usns_or_sha_changed.outputs.changed }} + needs: [ preparation, poll_usns, poll_images ] + if: ${{ !failure() && !cancelled() }} + steps: + - name: Check USNs or SHAs have changed + id: usns_or_sha_changed + run: | + if [ '${{ needs.poll_images.result }}' != 'skipped' ]; then + echo "Poll images job did not skip" + if [ '${{ needs.poll_images.outputs.images_need_update }}' = "true" ]; then + echo "SHAs have changed" + echo "changed=true" >> "$GITHUB_OUTPUT" + else + echo "SHAs have not changed" + echo "changed=false" >> "$GITHUB_OUTPUT" + fi + exit 0 + fi + + if [ '${{ needs.poll_usns.result }}' != 'skipped' ]; then + echo "Poll USNs did not skip" + if [ '${{ needs.poll_usns.outputs.usns }}' = "true" ]; then + echo "USNs have changed" + echo "changed=true" >> "$GITHUB_OUTPUT" + else + echo "USNs have not changed" + echo "changed=false" >> "$GITHUB_OUTPUT" + fi + exit 0 + fi + create_stack: name: Create Stack - needs: [ poll_images, preparation ] - if: ${{ ! ( needs.poll_images.outputs.images_need_update == null && github.event_name == 'schedule' ) }} + needs: [ preparation, usns_or_sha_changed ] + # If there is no change on the usns, and there is no change on the image hash codes + # and the event is schedule, then there is no need to run below workflow as nothing has changed + if: ${{ !failure() && !cancelled() && !( needs.usns_or_sha_changed.outputs.changed == 'false' && github.event_name == 'schedule') }} runs-on: ubuntu-22.04 strategy: matrix: @@ -207,6 +539,10 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Create stack ${{ matrix.stacks.name }} id: create-stack run: | @@ -220,15 +556,13 @@ jobs: --run-image "${{ matrix.stacks.output_dir }}/run.oci" \ --build-receipt current-build-receipt-${{ matrix.stacks.name }} \ --run-receipt current-run-receipt-${{ matrix.stacks.name }} - echo "build_receipt=current-build-receipt-${{ matrix.stacks.name }}" >> "$GITHUB_OUTPUT" - echo "run_receipt=current-run-receipt-${{ matrix.stacks.name }}" >> "$GITHUB_OUTPUT" - name: Upload run image uses: actions/upload-artifact@v4 with: name: current-run-image-${{ matrix.stacks.name }} path: "${{ matrix.stacks.output_dir }}/run.oci" - if-no-files-found: error + if-no-files-found: error - name: Upload build image if: ${{ matrix.stacks.create_build_image == true }} @@ -236,25 +570,26 @@ jobs: with: name: current-build-image-${{ matrix.stacks.name }} path: "${{ matrix.stacks.output_dir }}/build.oci" - if-no-files-found: error + if-no-files-found: error - name: Upload Build receipt if: ${{ matrix.stacks.create_build_image == true }} uses: actions/upload-artifact@v4 with: name: current-build-receipt-${{ matrix.stacks.name }} - path: ${{ steps.receipts.outputs.build_receipt }} + path: "*current-build-receipt-${{ matrix.stacks.name }}" if-no-files-found: error - name: Upload Run receipt uses: actions/upload-artifact@v4 with: name: current-run-receipt-${{ matrix.stacks.name }} - path: ${{ steps.receipts.outputs.run_receipt }} + path: "*current-run-receipt-${{ matrix.stacks.name }}" if-no-files-found: error diff: name: Diff Packages + if: ${{ !cancelled() && !failure() && needs.create_stack.result != 'skipped' }} outputs: removed_with_force: ${{ steps.removed_with_force.outputs.packages_removed }} needs: [ create_stack, preparation ] @@ -262,63 +597,106 @@ jobs: strategy: matrix: stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }} + arch: ${{ fromJSON(needs.preparation.outputs.architectures) }} steps: - - name: Download Build Receipt - if: ${{ matrix.stacks.create_build_image == true }} + - name: Download Current Receipt(s) uses: actions/download-artifact@v4 with: - name: current-build-receipt-${{ matrix.stacks.name }} + pattern: current-*-receipt-${{ matrix.stacks.name }} + path: receipt-files - - name: Download Run Receipt - uses: actions/download-artifact@v4 - with: - name: current-run-receipt-${{ matrix.stacks.name }} + - name: Move/Rename Build receipt properly + if: ${{ matrix.stacks.create_build_image == true }} + run: | + arch_prefix="${{ matrix.arch.name }}-" + if [[ "${{ matrix.arch.name }}" == "amd64" ]]; then + arch_prefix="" + fi + mv receipt-files/current-build-receipt-${{ matrix.stacks.name }}/${arch_prefix}current-build-receipt-${{ matrix.stacks.name }} ./${{ matrix.arch.name }}-current-build-receipt-${{ matrix.stacks.name }} + + - name: Move/Rename Run receipt properly + run: | + arch_prefix="${{ matrix.arch.name }}-" + if [[ "${{ matrix.arch.name }}" == "amd64" ]]; then + arch_prefix="" + fi + mv receipt-files/current-run-receipt-${{ matrix.stacks.name }}/${arch_prefix}current-run-receipt-${{ matrix.stacks.name }} ./${{ matrix.arch.name }}-current-run-receipt-${{ matrix.stacks.name }} + + - name: Check for Previous Releases + id: check_previous + run: | + gh auth status + # shellcheck disable=SC2046 + if [ $(gh api "/repos/${{ github.repository }}/releases" | jq -r 'length') -eq 0 ]; then + echo "exists=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "exists=true" >> "$GITHUB_OUTPUT" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Write Empty Previous Receipts + if: ${{ steps.check_previous.outputs.exists == 'false' || matrix.arch.is_new == true || matrix.stacks.is_new == true }} + run: | + if [ ${{ matrix.stacks.create_build_image }} == true ]; then + if [ ! -f "./${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" ]; then + echo '{"components":[]}' > "./${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" + fi + fi + if [ ! -f "./${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" ]; then + echo '{"components":[]}' > "./${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" + fi + + - name: Previous receipt download filename pattern + if: ${{ steps.check_previous.outputs.exists == 'true' && matrix.arch.is_new == false && matrix.stacks.is_new == false }} + id: previous_receipt_download_pattern + run: | + if [ "${{ matrix.arch.name }}" = "amd64" ]; then + if [ ${{ matrix.stacks.create_build_image }} == true ]; then + echo "build_filename=${{ needs.preparation.outputs.github_repo_name }}-\\d+\\.\\d+(\\.\\d+)?-${{ matrix.stacks.build_receipt_filename }}" >> "$GITHUB_OUTPUT" + fi + echo "run_filename=${{ needs.preparation.outputs.github_repo_name }}-\\d+\\.\\d+(\\.\\d+)?-${{ matrix.stacks.run_receipt_filename }}" >> "$GITHUB_OUTPUT" + else + if [ ${{ matrix.stacks.create_build_image }} == true ]; then + echo "build_filename=${{ matrix.arch.name }}-${{ matrix.stacks.build_receipt_filename }}" >> "$GITHUB_OUTPUT" + fi + echo "run_filename=${{ matrix.arch.name }}-${{ matrix.stacks.run_receipt_filename }}" >> "$GITHUB_OUTPUT" + fi - name: Find and Download Previous Build Receipt - id: download_previous_build_receipt - if: ${{ matrix.stacks.create_build_image == true }} + if: ${{ matrix.stacks.create_build_image == true && steps.check_previous.outputs.exists == 'true' && matrix.arch.is_new == false && matrix.stacks.is_new == false }} uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main with: - asset_pattern: "${{ matrix.stacks.build_receipt_filename }}" + asset_pattern: "${{ steps.previous_receipt_download_pattern.outputs.build_filename }}" search_depth: 1 repo: ${{ github.repository }} - output_path: "/github/workspace/previous-build-receipt-${{ matrix.stacks.name }}" + output_path: "/github/workspace/${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} - name: Find and Download Previous Run Receipt - id: download_previous_run_receipt + if: ${{ steps.check_previous.outputs.exists == 'true' && matrix.arch.is_new == false && matrix.stacks.is_new == false }} uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main with: - asset_pattern: "${{ matrix.stacks.run_receipt_filename }}" + asset_pattern: "${{ steps.previous_receipt_download_pattern.outputs.run_filename }}" search_depth: 1 repo: ${{ github.repository }} - output_path: "/github/workspace/previous-run-receipt-${{ matrix.stacks.name }}" + output_path: "/github/workspace/${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} - - name: Write Empty Previous Run Receipts - if: ${{ steps.download_previous_run_receipt.outputs.output_path == '' }} - run: | - echo '{"components":[]}' > "${{ github.workspace }}/previous-run-receipt-${{ matrix.stacks.name }}" - - - name: Write Empty Previous Build Receipts - if: ${{ matrix.stacks.create_build_image == true && steps.download_previous_build_receipt.outputs.output_path == '' }} - run: | - echo '{"components":[]}' > "${{ github.workspace }}/previous-build-receipt-${{ matrix.stacks.name }}" - - name: Compare Build Packages id: build_diff if: ${{ matrix.stacks.create_build_image == true }} uses: paketo-buildpacks/github-config/actions/stack/diff-package-receipts@main with: - previous: "/github/workspace/previous-build-receipt-${{ matrix.stacks.name }}" - current: "/github/workspace/current-build-receipt-${{ matrix.stacks.name }}" + previous: "/github/workspace/${{ matrix.arch.name }}-previous-build-receipt-${{ matrix.stacks.name }}" + current: "/github/workspace/${{ matrix.arch.name }}-current-build-receipt-${{ matrix.stacks.name }}" - name: Compare Run Packages id: run_diff uses: paketo-buildpacks/github-config/actions/stack/diff-package-receipts@main with: - previous: "/github/workspace/previous-run-receipt-${{ matrix.stacks.name }}" - current: "/github/workspace/current-run-receipt-${{ matrix.stacks.name }}" + previous: "/github/workspace/${{ matrix.arch.name }}-previous-run-receipt-${{ matrix.stacks.name }}" + current: "/github/workspace/${{ matrix.arch.name }}-current-run-receipt-${{ matrix.stacks.name }}" - name: Fail If Packages Removed id: removed_with_force @@ -332,13 +710,15 @@ jobs: echo "Run packages removed: ${run}" # only fail if packages are removed AND the release has not been forced - if ([ "${build}" -gt 0 ] && [ "${{ matrix.stacks.create_build_image }}" == "true" ]) || [ "${run}" -gt 0 ]; then + if ( [ "${build}" -gt 0 ] && [ "${{ matrix.stacks.create_build_image }}" == "true" ] ) || [ "${run}" -gt 0 ]; then if [ "${{ github.event.inputs.force }}" != 'true' ]; then echo "Packages removed without authorization. Stack cannot be released." exit 1 else echo "packages_removed=true" >> "$GITHUB_OUTPUT" fi + else + echo "packages_removed=false" >> "$GITHUB_OUTPUT" fi env: BUILD_REMOVED: ${{ steps.build_diff.outputs.removed }} @@ -347,8 +727,8 @@ jobs: - name: Create/Upload variable artifacts id: variable_artifacts run: | - mkdir -p diff-${{ matrix.stacks.name }} - cd diff-${{ matrix.stacks.name }} + mkdir -p diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }} + cd diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }} if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then echo '${{ steps.build_diff.outputs.added }}' > build_added @@ -360,11 +740,94 @@ jobs: echo '${{ steps.run_diff.outputs.modified }}' > run_modified echo '${{ steps.run_diff.outputs.removed }}' > run_removed_with_force - - name: Upload diff-${{ matrix.stacks.name }} + - name: Upload diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }} + uses: actions/upload-artifact@v4 + with: + name: diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }} + path: diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }} + if-no-files-found: error + + - name: Download USN File(s) + if: ${{ needs.preparation.outputs.polling_type == 'usn' }} + uses: actions/download-artifact@v4 + with: + path: "patched-usns" + pattern: ${{ matrix.arch.name }}-*${{ env.PATCHED_USNS_FILENAME }} + merge-multiple: true + + - name: Get USNs + id: get-usns + if: ${{ needs.preparation.outputs.polling_type == 'usn' }} + run: | + usns=$(cat "patched-usns/${{ matrix.arch.name }}-${{ matrix.stacks.name }}-${{ env.PATCHED_USNS_FILENAME }}" | jq -c ) + echo "usns=$usns" >> "$GITHUB_OUTPUT" + + - name: Get Repository Name + id: repo_name + run: | + full=${{ github.repository }} + # Strip off the org and slash from repo name + # paketo-buildpacks/repo-name --> repo-name + repo=$(echo "${full}" | sed 's/^.*\///') + echo "github_repo_name=${repo}" >> "$GITHUB_OUTPUT" + + # Strip off 'stack' suffix from repo name + # some-name-stack --> some-name + registry_repo="${repo//-stack/}" + echo "registry_repo_name=${registry_repo}" >> "$GITHUB_OUTPUT" + + - name: Increment Tag + if: github.event.inputs.version == '' + id: semver + uses: paketo-buildpacks/github-config/actions/tag/increment-tag@main + with: + allow_head_tagged: true + + - name: Set Release Tag + id: tag + run: | + tag="${{ github.event.inputs.version }}" + if [ -z "${tag}" ]; then + tag="${{ steps.semver.outputs.tag }}" + fi + echo "tag=${tag}" >> "$GITHUB_OUTPUT" + + - name: Get registry build and run image names + id: registry_names + run: | + if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then + echo "build_image=${{ needs.preparation.outputs.repo_owner }}/${{ matrix.stacks.build_image }}-${{ needs.preparation.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}" >> "$GITHUB_OUTPUT" + else + echo "build_image=" >> "$GITHUB_OUTPUT" + fi + + echo "run_image=${{ needs.preparation.outputs.repo_owner }}/${{ matrix.stacks.run_image }}-${{ needs.preparation.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}" >> "$GITHUB_OUTPUT" + + - name: Create Release Notes per Arch and per Stack + id: notes_arc_stack + uses: paketo-buildpacks/github-config/actions/stack/release-notes@main + with: + build_image: ${{ steps.registry_names.outputs.build_image }} + run_image: ${{ steps.registry_names.outputs.run_image }} + build_packages_added: ${{ steps.build_diff.outputs.added }} + build_packages_modified: ${{ steps.build_diff.outputs.modified }} + build_packages_removed_with_force: ${{ steps.build_diff.outputs.removed }} + run_packages_added: ${{ steps.run_diff.outputs.added }} + run_packages_modified: ${{ steps.run_diff.outputs.modified }} + run_packages_removed_with_force: ${{ steps.run_diff.outputs.removed }} + supports_usns: ${{ needs.preparation.outputs.support_usns }} + patched_usns: ${{ needs.get-usns.outputs.usns }} + + - name: Release Notes File + id: release-notes-file + run: | + printf '%s\n' '${{ steps.notes_arc_stack.outputs.release_body }}' > "${{ matrix.arch.name }}-${{ matrix.stacks.name }}-release-notes.md" + + - name: Upload ${{ matrix.arch.name }} release notes file for stack ${{ matrix.stacks.name }} uses: actions/upload-artifact@v4 with: - name: diff-${{ matrix.stacks.name }} - path: diff-${{ matrix.stacks.name }} + name: "${{ matrix.arch.name }}-${{ matrix.stacks.name }}-release-notes.md" + path: "${{ matrix.arch.name }}-${{ matrix.stacks.name }}-release-notes.md" run_if_packages_removed_with_force: name: Run If Packages Removed With Force @@ -380,36 +843,48 @@ jobs: name: Determine If Packages Changed needs: [ diff, preparation ] runs-on: ubuntu-22.04 + if: ${{ !cancelled() && !failure() && needs.diff.result != 'skipped' }} strategy: matrix: stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }} + arch: ${{ fromJSON(needs.preparation.outputs.architectures) }} outputs: packages_changed: ${{ steps.compare.outputs.packages_changed }} steps: - - name: Download diff-${{ matrix.stacks.name }} + - name: Download diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }} uses: actions/download-artifact@v4 with: - name: diff-${{ matrix.stacks.name }} + name: diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }} - - name: Create env variables + - name: Set env diff variables run: | + if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then - echo "BUILD_ADDED=$(cat build_added)" >> $GITHUB_ENV - echo "BUILD_MODIFIED=$(cat build_modified)" >> $GITHUB_ENV + BUILD_ADDED=$(cat ./build_added) + BUILD_MODIFIED=$(cat ./build_modified) + echo "BUILD_ADDED=$BUILD_ADDED" >> $GITHUB_ENV + echo "BUILD_MODIFIED=$BUILD_MODIFIED" >> $GITHUB_ENV + else + echo "BUILD_ADDED=$BUILD_ADDED" >> $GITHUB_ENV + echo "BUILD_MODIFIED=$BUILD_MODIFIED" >> $GITHUB_ENV fi - echo "RUN_ADDED=$(cat run_added)" >> $GITHUB_ENV - echo "RUN_MODIFIED=$(cat run_modified)" >> $GITHUB_ENV + + RUN_ADDED=$(cat ./run_added) + RUN_MODIFIED=$(cat ./run_modified) + echo "RUN_ADDED=$RUN_ADDED" >> $GITHUB_ENV + echo "RUN_MODIFIED=$RUN_MODIFIED" >> $GITHUB_ENV - name: Compare With Previous Release id: compare run: | + # shellcheck disable=SC2153 + if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then - # shellcheck disable=SC2153 - build_added=$(jq '. | length' <<< "${BUILD_ADDED}") + build_added=$(cat ./build_added | jq 'length') echo "Build packages added: ${build_added}" # shellcheck disable=SC2153 - build_modified=$(jq '. | length' <<< "${BUILD_MODIFIED}") + build_modified=$(cat ./build_modified | jq 'length') echo "Build packages modified: ${build_modified}" fi @@ -421,31 +896,20 @@ jobs: run_modified=$(jq '. | length' <<< "${RUN_MODIFIED}") echo "Run packages modified: ${run_modified}" - if [ "${run_added}" -eq 0 ] && [ "${run_modified}" -eq 0 ]; then + if [ "${build_added}" -eq 0 ] && [ "${build_modified}" -eq 0 ] && [ "${run_added}" -eq 0 ] && [ "${run_modified}" -eq 0 ]; then echo "No packages changed." - # we dont set the "packages_changed" to false, as is already null - # and also due to parallelization, it will override any true value + # We ommit setting "packages_changed" variable to false, + # as there is an edge case scenario overriding any true value due to parallelization else echo "Packages changed." echo "packages_changed=true" >> "$GITHUB_OUTPUT" fi - if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then - if [ "${build_added}" -eq 0 ] && [ "${build_modified}" -eq 0 ]; then - echo "No packages changed." - # we dont set the "packages_changed" to false, as is already null - # and also due to parallelization, it will override the value - else - echo "Packages changed." - echo "packages_changed=true" >> "$GITHUB_OUTPUT" - fi - fi - run_if_packages_changed: name: Run If Packages Changed runs-on: ubuntu-22.04 needs: [packages_changed] - if: ${{ needs.packages_changed.outputs.packages_changed == 'true' }} + if: ${{ needs.packages_changed.outputs.packages_changed == 'true' && !cancelled() }} steps: - name: Run if packages changed run: | @@ -453,31 +917,27 @@ jobs: test: name: Acceptance Test - needs: [ create_stack, preparation ] + needs: [ preparation, create_stack ] + if: ${{ !cancelled() && !failure() && needs.create_stack.result != 'skipped' }} runs-on: ubuntu-22.04 steps: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: 'stable' + go-version: stable - name: Checkout uses: actions/checkout@v4 - - - name: tests ran - run: echo "tests ran" - - name: Download Build images + - name: Download Build Image(s) uses: actions/download-artifact@v4 with: - pattern: | - current-build-image-* - - - name: Download Run images + pattern: current-build-image-* + + - name: Download Run Image(s) uses: actions/download-artifact@v4 with: - pattern: | - current-run-image-* + pattern: current-run-image-* - name: Create OCI artifacts destination directory run: | @@ -493,7 +953,7 @@ jobs: done - name: Run Acceptance Tests - run: ./scripts/test.sh + run: ./scripts/test.sh --validate-stack-builds force_release_creation: name: Force Release Creation @@ -509,9 +969,6 @@ jobs: runs-on: ubuntu-22.04 needs: [create_stack, diff, run_if_stack_files_changed, run_if_packages_changed, run_if_packages_removed_with_force, test, force_release_creation, preparation ] if: ${{ always() && needs.diff.result == 'success' && needs.test.result == 'success' && (needs.run_if_packages_changed.result == 'success' || needs.run_if_stack_files_changed.result == 'success' || needs.force_release_creation.result == 'success' ) }} - strategy: - matrix: - stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }} outputs: tag: ${{ steps.tag.outputs.tag }} steps: @@ -529,6 +986,72 @@ jobs: with: fetch-depth: 0 # gets full history + - name: Download current build image(s) + uses: actions/download-artifact@v4 + with: + path: image-files + pattern: current-build-image-* + + - name: Download current run image(s) + uses: actions/download-artifact@v4 + with: + path: image-files + pattern: current-run-image-* + + - name: Display Build and Run Images + run: ls image-files + + - name: Download Build Receipt(s) + uses: actions/download-artifact@v4 + with: + path: receipt-files + pattern: current-build-receipt-* + merge-multiple: true + + - name: Download Run Receipt(s) + uses: actions/download-artifact@v4 + with: + path: receipt-files + pattern: current-run-receipt-* + merge-multiple: true + + - name: Display Receipts + run: ls receipt-files + + - name: Download Release Note File(s) + uses: actions/download-artifact@v4 + with: + path: release-notes + pattern: "*release-notes.md" + merge-multiple: true + + - name: Display Release Note Files + run: ls release-notes + + - name: Download hash code Files + if: ${{ needs.preparation.outputs.polling_type == 'hash' }} + uses: actions/download-artifact@v4 + with: + path: hash-code-files + pattern: hash-code-* + merge-multiple: true + + - name: Display hash code Files + if: ${{ needs.preparation.outputs.polling_type == 'hash' }} + run: ls hash-code-files + + - name: Download USN Files + if: ${{ needs.preparation.outputs.polling_type == 'usn' }} + uses: actions/download-artifact@v4 + with: + path: usn-files + pattern: "*${{ env.PATCHED_USNS_FILENAME }}" + merge-multiple: true + + - name: Display USN Files + if: ${{ needs.preparation.outputs.polling_type == 'usn' }} + run: ls usn-files + - name: Increment Tag if: github.event.inputs.version == '' id: semver @@ -545,221 +1068,291 @@ jobs: fi echo "tag=${tag}" >> "$GITHUB_OUTPUT" - - name: Get Repository Name - id: repo_name - run: | - full=${{ github.repository }} - # Strip off the org and slash from repo name - # paketo-buildpacks/jammy-base-stack --> jammy-base-stack - repo=$(echo "${full}" | sed 's/^.*\///') - echo "github_repo_name=${repo}" >> "$GITHUB_OUTPUT" - # Strip off 'stack' suffix from repo name - # paketo-buildpacks/jammy-base-stack --> jammy-base - registry_repo="${repo//-stack/}" - echo "registry_repo_name=${registry_repo}" >> "$GITHUB_OUTPUT" - - - name: Download diff-${{ matrix.stacks.name }} - uses: actions/download-artifact@v4 - with: - name: diff-${{ matrix.stacks.name }} - - - name: Release notes preparation - id: release_notes_preparation - run: | - if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then - echo "build_image=${{ needs.preparation.outputs.repo_owner }}/${{ matrix.stacks.build_image }}-${{ needs.preparation.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}" >> "$GITHUB_OUTPUT" - echo "build_added=$(jq -c <<< "$(cat build_added)" )" >> "$GITHUB_OUTPUT" - echo "build_modified=$(jq -c <<< "$(cat build_modified)" )" >> "$GITHUB_OUTPUT" - echo "build_removed_with_force=$(jq -c <<< "$(cat build_removed_with_force)" )" >> "$GITHUB_OUTPUT" - else - echo "build_image=" >> "$GITHUB_OUTPUT" - echo "build_added=" >> "$GITHUB_OUTPUT" - echo "build_modified=" >> "$GITHUB_OUTPUT" - echo "build_removed_with_force=" >> "$GITHUB_OUTPUT" - fi - - echo "run_image=${{ needs.preparation.outputs.repo_owner }}/${{ matrix.stacks.run_image }}-${{ needs.preparation.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}" >> "$GITHUB_OUTPUT" - echo "run_added=$(jq -c <<< "$(cat run_added)" )" >> "$GITHUB_OUTPUT" - echo "run_modified=$(jq -c <<< "$(cat run_modified)" )" >> "$GITHUB_OUTPUT" - echo "run_removed_with_force=$(jq -c <<< "$(cat run_removed_with_force)" )" >> "$GITHUB_OUTPUT" - - - name: Fetch Receipts show limit - id: fetch_receipts_show_limit - run: | - if [[ -f ${{ env.STACKS_FILENAME }} ]]; then - echo "receipts_show_limit=$( jq -r '.receipts_show_limit // ""' ${{ env.STACKS_FILENAME }} )" >> "$GITHUB_OUTPUT" - fi - - - name: Create Release Notes - id: notes - uses: paketo-buildpacks/github-config/actions/stack/release-notes@main - with: - build_image: ${{ steps.release_notes_preparation.outputs.build_image }} - run_image: ${{ steps.release_notes_preparation.outputs.run_image }} - build_packages_added: ${{ steps.release_notes_preparation.outputs.build_added }} - build_packages_modified: ${{ steps.release_notes_preparation.outputs.build_modified }} - build_packages_removed_with_force: ${{ steps.release_notes_preparation.outputs.build_removed_with_force }} - run_packages_added: ${{ steps.release_notes_preparation.outputs.run_added }} - run_packages_modified: ${{ steps.release_notes_preparation.outputs.run_modified }} - run_packages_removed_with_force: ${{ steps.release_notes_preparation.outputs.run_removed_with_force }} - receipts_show_limit: ${{ steps.fetch_receipts_show_limit.outputs.receipts_show_limit }} - supports_usns: ${{ needs.preparation.outputs.support_usns }} - - name: Setup Release Assets id: assets run: | - assets="$(jq --null-input --compact-output \ - --arg tag "${{ steps.tag.outputs.tag }}" \ - --arg repo "${{ steps.repo_name.outputs.github_repo_name }}" \ - --arg build_receipt "${{ matrix.stacks.build_receipt_filename }}" \ - --arg run_receipt "${{ matrix.stacks.run_receipt_filename }}" \ - '[ - { - "path": ("current-build-image-${{ matrix.stacks.name }}/build" + ".oci"), - "name": ($repo + "-" + $tag + "-" + "${{ matrix.stacks.build_image }}.oci"), - "content_type": "application/gzip" - }, - { - "path": ("current-run-image-${{ matrix.stacks.name }}/run" + ".oci"), - "name": ($repo + "-" + $tag + "-" + "${{ matrix.stacks.run_image }}.oci"), - "content_type": "application/gzip" - }, - { - "path": ("current-build-receipt-${{ matrix.stacks.name }}"), - "name": ($repo + "-" + $tag + "-" + $build_receipt), - "content_type": "text/plain" - }, - { - "path": ("current-run-receipt-${{ matrix.stacks.name }}"), - "name": ($repo + "-" + $tag + "-" + $run_receipt), - "content_type": "text/plain" - }, - { - "path": ("hash-code-current-build-image-${{ matrix.stacks.name }}"), - "name": ($repo + "-" + $tag + "-" + "${{ matrix.stacks.build_image }}.oci.sha256"), - "content_type": "application/gzip" - }, - { - "path": ("hash-code-current-run-image-${{ matrix.stacks.name }}"), - "name": ($repo + "-" + $tag + "-" + "${{ matrix.stacks.run_image }}.oci.sha256"), - "content_type": "application/gzip" - }]')" - - # Exclude/filter the assets that are not required for the release. - if [ "${{ matrix.stacks.create_build_image }}" == "false" ]; then - assets=$(jq -c --arg stack_name "${{ matrix.stacks.name }}" '[ - .[] - | select( - .path - | (contains("current-build-image-" + $stack_name + "/build.oci") - or contains("hash-code-current-build-image-" + $stack_name) - or contains("current-build-receipt-" + $stack_name)) - | not - ) - ]' <<< "$assets") - fi - echo "${assets}" > release-assets-${{ matrix.stacks.name }} + stacks=$(echo '${{ needs.preparation.outputs.stacks }}' | jq -c '.[]') + archs=$(echo '${{ needs.preparation.outputs.architectures }}' | jq -c -r '.[]') + repo="${{ needs.preparation.outputs.github_repo_name }}" + tag="${{ steps.tag.outputs.tag }}" + + # Start with an empty array + assets=$(jq -n -c '[]') + + for stack in $stacks; do + stack_name=$(echo "$stack" | jq -r '.name') + run_image=$(echo "$stack" | jq -r '.run_image') + create_build_image=$(echo "$stack" | jq -r '.create_build_image // false') + run_receipt_filename=$(echo "$stack" | jq -r '.run_receipt_filename') + + assets="$(jq -c \ + --arg image_filepath "image-files" \ + --arg stack_name "${stack_name}" \ + --arg run_image "${run_image}" \ + --arg tag "${{ steps.tag.outputs.tag }}" \ + --arg repo "${{ needs.preparation.outputs.github_repo_name }}" \ + '. += [ + { + "path": ($image_filepath + "/" + "current-run-image-" + $stack_name + "/run" + ".oci"), + "name": ($repo + "-" + $tag + "-" + $run_image + ".oci"), + "content_type": "application/gzip" + } + ]' <<<"${assets}")" + + if [[ $create_build_image == true ]]; then + build_image=$(echo "$stack" | jq -r '.build_image') + assets="$(jq -c \ + --arg image_filepath "image-files" \ + --arg stack_name "${stack_name}" \ + --arg build_image "${build_image}" \ + --arg tag "${{ steps.tag.outputs.tag }}" \ + --arg repo "${{ needs.preparation.outputs.github_repo_name }}" \ + '. += [ + { + "path": ($image_filepath + "/" + "current-build-image-" + $stack_name + "/build" + ".oci"), + "name": ($repo + "-" + $tag + "-" + $build_image + ".oci"), + "content_type": "application/gzip" + } + ]' <<<"${assets}")" + fi + + for arch in $archs; do + arch_name=$(echo "$arch" | jq -r '.name') + arch_prefix="-${arch_name}-" + if [[ $arch_name == "amd64" ]]; then + arch_prefix="-" + fi + + if [ "${{ needs.preparation.outputs.polling_type }}" = "hash" ]; then + ## Add the Hash code files of the run images to the assets + run_image=$(echo "$stack" | jq -r '.run_image') + assets="$(jq -c \ + --arg hash_code_filepath "hash-code-files" \ + --arg stack_name "${stack_name}" \ + --arg run_image "${run_image}" \ + --arg tag "${{ steps.tag.outputs.tag }}" \ + --arg repo "${{ needs.preparation.outputs.github_repo_name }}" \ + --arg arch "${arch_name}" \ + --arg arch_prefix "${arch_prefix}" \ + '. += [ + { + "path": ($hash_code_filepath + "/" + "hash-code-current-run-image-"+ $arch + "-" + $stack_name ), + "name": ($repo + "-" + $tag + $arch_prefix + $run_image + ".oci.sha256"), + "content_type": "text/plain" + } + ]' <<<"${assets}")" + + ## Add the Hash code files of the build images to the assets + if [[ $create_build_image == true ]]; then + build_image=$(echo "$stack" | jq -r '.build_image') + assets="$(jq -c \ + --arg hash_code_filepath "hash-code-files" \ + --arg stack_name "${stack_name}" \ + --arg build_image "${build_image}" \ + --arg tag "${{ steps.tag.outputs.tag }}" \ + --arg repo "${{ needs.preparation.outputs.github_repo_name }}" \ + --arg arch "${arch_name}" \ + --arg arch_prefix "${arch_prefix}" \ + '. += [ + { + "path": ($hash_code_filepath + "/" + "hash-code-current-build-image-"+ $arch + "-" + $stack_name ), + "name": ($repo + "-" + $tag + $arch_prefix + $build_image + ".oci.sha256"), + "content_type": "text/plain" + } + ]' <<<"${assets}")" + fi + fi + done + done - - name: Save release notes - id: save_notes - run: | - echo '${{ steps.notes.outputs.release_body }}' > release-body-${{ matrix.stacks.name }} + ## Adding the SBOM files to the assets + for stack in $stacks; do + for arch in $archs; do + arch_name=$(echo "$arch" | jq -r '.name') - - name: Upload release notes of ${{ matrix.stacks.name }} - uses: actions/upload-artifact@v4 - with: - name: release-body-${{ matrix.stacks.name }} - path: release-body-${{ matrix.stacks.name }} + stack_name=$(echo "$stack" | jq -r '.name') + run_receipt_filename=$(echo "$stack" | jq -r '.run_receipt_filename') - - name: Upload release assets of ${{ matrix.stacks.name }} - uses: actions/upload-artifact@v4 - with: - name: release-assets-${{ matrix.stacks.name }} - path: release-assets-${{ matrix.stacks.name }} + receipt_file_name=$( + echo '[ "'"$arch_name"'" ,"current-run-receipt" , "'"$stack_name"'" ]' | + jq -r 'map(select(. != "amd64")) | join("-")' + ) - create_release: - name: Create Release - runs-on: ubuntu-22.04 - needs: release - if: ${{ always() && needs.release.result == 'success' }} - steps: - - name: download release assets - uses: actions/download-artifact@v4 - with: - pattern: release-assets-* - merge-multiple: true + receipt_asset_name=$( + echo '[ "'"$repo"'", "'"$tag"'", "'"$arch_name"'", "'"$run_receipt_filename"'" ]' | + jq -r 'map(select(. != "amd64")) | map(select(. != "default")) | join("-")' + ) - - name: Merge release assets - id: merge_release_assets - run: | - merged_release_assets='[]' - for file in release-assets-*; do - merged_release_assets=$(jq -s add "$file" <(echo "$merged_release_assets") | jq -c) + assets="$(jq -c \ + --arg receipts_filepath "receipt-files" \ + --arg receipt_file_name "${receipt_file_name}" \ + --arg receipt_asset_name "${receipt_asset_name}" \ + '. += [ + { + "path": ($receipts_filepath + "/" + $receipt_file_name), + "name": $receipt_asset_name, + "content_type": "text/plain" + } + ]' <<<"${assets}")" + + create_build_image=$(echo "$stack" | jq -r '.create_build_image // false') + + if [[ $create_build_image == true ]]; then + build_receipt_filename=$(echo "$stack" | jq -r '.build_receipt_filename') + + receipt_file_name=$( + echo '[ "'"$arch_name"'" ,"current-build-receipt" , "'"$stack_name"'" ]' | + jq -r 'map(select(. != "amd64")) | join("-")' + ) + + receipt_asset_name=$( + echo '[ "'"$repo"'", "'"$tag"'", "'"$arch_name"'", "'"$build_receipt_filename"'" ]' | + jq -r 'map(select(. != "amd64")) | map(select(. != "default")) | join("-")' + ) + + assets="$(jq -c \ + --arg receipts_filepath "receipt-files" \ + --arg receipt_file_name "${receipt_file_name}" \ + --arg receipt_asset_name "${receipt_asset_name}" \ + --arg tag "${{ steps.tag.outputs.tag }}" \ + --arg repo "${{ needs.preparation.outputs.github_repo_name }}" \ + --arg arch_prefix "${arch_prefix}" \ + '. += [ + { + "path": ($receipts_filepath + "/" + $receipt_file_name), + "name": $receipt_asset_name, + "content_type": "text/plain" + } + ]' <<<"${assets}")" + fi + done done - printf "assets=%s\n" "${merged_release_assets}" >> "$GITHUB_OUTPUT" + ## Add the usn files to the assets + if [ "${{ needs.preparation.outputs.polling_type }}" = "usn" ]; then + for stack in $stacks; do + stack_name=$(echo "$stack" | jq -r '.name') + stack_name_prefix="${stack}" + if [[ $stack_name == "default" ]]; then + stack_name_prefix="" + fi + + for arch in $archs; do + arch_name=$(echo "$arch" | jq -r '.name') + arch_prefix="${arch_name}" + if [[ $arch_name == "amd64" ]]; then + arch_prefix="" + fi + + usn_asset_name=$( + echo '[ + "${{ needs.preparation.outputs.github_repo_name }}", + "${{ steps.tag.outputs.tag }}", + "'"$stack_name_prefix"'", + "'"$arch_prefix"'", + "${{ env.PATCHED_USNS_FILENAME }}" + ]' | jq -r 'map(select(length > 0)) | join("-")' + ) + + assets="$(jq -c \ + --arg usns_filepath "usn-files" \ + --arg stack_name "${stack_name}" \ + --arg arch "${arch_name}" \ + --arg usn_asset_name "${usn_asset_name}" \ + --arg patched_usns_suffix "${{ env.PATCHED_USNS_FILENAME }}" \ + '. += [ + { + "path": ($usns_filepath + "/" + $arch + "-" + $stack_name + "-" + $patched_usns_suffix), + "name": $usn_asset_name, + "content_type": "text/plain" + } + ]' <<< "${assets}")" + done + done + fi - - name: Download release bodies - uses: actions/download-artifact@v4 - with: - pattern: release-body-* - merge-multiple: true + # Merge relase notes per architecture and add them to the assets + release_notes_dir="release-notes" + for arch in $archs; do + arch_name=$(echo "$arch" | jq -r '.name') + arch_prefix="-${arch_name}-" + if [[ $arch_name == "amd64" ]]; then + arch_prefix="-" + fi - - name: Merge release bodies - id: merge_release_bodies - run: | - touch release_notes - for filename in release-body-*; do - cat "$filename" >> release_notes - echo >> release_notes + # Merge the release notes per architecture + for stack in $stacks; do + stack_name=$(echo "$stack" | jq -r '.name') + filename="${arch_name}-${stack_name}-release-notes.md" + cat "${release_notes_dir}/${filename}" >>"${release_notes_dir}/${arch_name}-release-notes" + done + + # add release notes of the arch on the assets + assets="$(jq -c \ + --arg release_notes_dir "${release_notes_dir}" \ + --arg tag "${{ steps.tag.outputs.tag }}" \ + --arg repo "${{ needs.preparation.outputs.github_repo_name }}" \ + --arg arch "${arch_name}" \ + --arg arch_prefix "${arch_prefix}" \ + '. += [ + { + "path": ($release_notes_dir + "/" + $arch + "-" + "release-notes"), + "name": ($repo + "-" + $tag + $arch_prefix + "release-notes.md"), + "content_type": "text/plain" + } + ]' <<<"${assets}")" done - - name: Download current build images - uses: actions/download-artifact@v4 - with: - pattern: current-build-image-* + echo "assets=${assets}" >> "$GITHUB_OUTPUT" - - name: Download current run images - uses: actions/download-artifact@v4 - with: - pattern: current-run-image-* + - name: Generate Release Notes Description + id: notes + run: | - - name: Download current hash codes - uses: actions/download-artifact@v4 - with: - pattern: hash-code-* - merge-multiple: true + release_notes_dir="release-notes" - - name: Download Build Receipts - uses: actions/download-artifact@v4 - with: - pattern: current-build-receipt-* - merge-multiple: true + # If there is only one architecture + archs_length=$(echo '${{ needs.preparation.outputs.architectures }}' | jq 'length') + if [ $archs_length -eq 1 ]; then + arch_name=$(echo '${{ needs.preparation.outputs.architectures }}' | jq -r '.[0].name') + cat "${release_notes_dir}/${arch_name}-release-notes" > release_notes.md + else + echo "## Images" > release_notes.md + echo "" >> release_notes.md + stacks=$(echo '${{ needs.preparation.outputs.stacks }}' | jq -c -r '.[]') + for stack in $stacks; do + stack_name=$(echo "$stack" | jq -r '.name') + run_image=$(echo "$stack" | jq -r '.run_image') + build_image=$(echo "$stack" | jq -r '.build_image') + create_build_image=$(echo "$stack" | jq -r '.create_build_image // false') + + if [ ${create_build_image} == true ]; then + echo "Build: \`${{ needs.preparation.outputs.repo_owner }}/${build_image}-${{ needs.preparation.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}\`" >> release_notes.md + fi + echo "Run: \`${{ needs.preparation.outputs.repo_owner }}/${run_image}-${{ needs.preparation.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}\`" >> release_notes.md + done + fi - - name: Download Run Receipts - uses: actions/download-artifact@v4 - with: - pattern: current-run-receipt-* - merge-multiple: true + echo "release_body=release_notes.md" >> "$GITHUB_OUTPUT" - name: Create Release uses: paketo-buildpacks/github-config/actions/release/create@main with: repo: ${{ github.repository }} token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} - tag_name: v${{ needs.release.outputs.tag }} + tag_name: v${{ steps.tag.outputs.tag }} target_commitish: ${{ github.sha }} - name: v${{ needs.release.outputs.tag }} - body_filepath: release_notes + name: v${{ steps.tag.outputs.tag }} + body_filepath: ${{ steps.notes.outputs.release_body }} draft: false - assets: ${{ steps.merge_release_assets.outputs.assets }} + assets: ${{ steps.assets.outputs.assets }} failure: name: Alert on Failure runs-on: ubuntu-22.04 - needs: [preparation, poll_images, create_stack, diff, test, release, create_release, packages_changed, stack_files_changed] - if: ${{ always() && needs.preparation.result == 'failure' || needs.poll_images.result == 'failure' || needs.create_stack.result == 'failure' || needs.diff.result == 'failure' || needs.test.result == 'failure' || needs.release.result == 'failure' || needs.create_release.result == 'failure' || needs.packages_changed.result == 'failure' || needs.stack_files_changed.result == 'failure' }} + needs: [ preparation, poll_usns, poll_images, create_stack, diff, test, release, packages_changed, stack_files_changed ] + if: ${{ always() && needs.preparation.result == 'failure' || needs.poll_images.result == 'failure' || needs.poll_usns.result == 'failure' || needs.create_stack.result == 'failure' || needs.diff.result == 'failure' || needs.test.result == 'failure' || needs.release.result == 'failure' || needs.packages_changed.result == 'failure' || needs.stack_files_changed.result == 'failure' }} steps: - name: File Failure Alert Issue uses: paketo-buildpacks/github-config/actions/issue/file@main @@ -773,4 +1366,4 @@ jobs: Create Release workflow [failed](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). Unable to update images. comment_body: | - Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} \ No newline at end of file + Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/push-image.yml b/.github/workflows/push-image.yml index fd8b4ec..145ee79 100644 --- a/.github/workflows/push-image.yml +++ b/.github/workflows/push-image.yml @@ -4,54 +4,85 @@ on: release: types: - published + + workflow_dispatch: + inputs: + version: + description: 'Version of the stack to push' + required: false + env: - REGISTRIES_FILENAME: "registries.json" + REGISTRIES_FILEPATH: "registries.json" + GCR_REGISTRY: "gcr.io" + GCR_USERNAME: "_json_key" jobs: preparation: name: Preparation - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} - DOCKERHUB_ORG: ${{ steps.set-dockerhub-org-namespace.outputs.DOCKERHUB_ORG}} - push_to_gcr: ${{ steps.parse_configs.outputs.push_to_gcr}} - push_to_dockerhub: ${{ steps.parse_configs.outputs.push_to_dockerhub}} + DOCKERHUB_ORG: ${{ steps.set-dockerhub-org-namespace.outputs.DOCKERHUB_ORG }} + push_to_gcr: ${{ steps.parse_configs.outputs.push_to_gcr }} + push_to_dockerhub: ${{ steps.parse_configs.outputs.push_to_dockerhub }} + tag: ${{ steps.event.outputs.tag }} + repo_name: ${{ steps.registry-repo.outputs.repo_name }} steps: - name: Checkout uses: actions/checkout@v4 - - name: Set matrix - id: set-matrix + - name: Parse Event + id: event run: | - release_version="$(jq -r '.release.tag_name' "${GITHUB_EVENT_PATH}" | sed s/^v//)" + set -euo pipefail + shopt -s inherit_errexit + + # If the workflow has been triggered from dispatch event + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "tag=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT" + else #The workflow has been triggered from publish event + echo "tag=$(jq -r '.release.tag_name' "${GITHUB_EVENT_PATH}" | sed s/^v//)" >> "$GITHUB_OUTPUT" + fi + - name: Get Registry Repo Name + id: registry-repo + run: | # Strip off the org and slash from repo name # paketo-buildpacks/repo-name --> repo-name - repo_name=$(echo "${{ github.repository }}" | sed 's/^.*\///') + echo "repo_name=$(echo "${{ github.repository }}" | sed 's/^.*\///')" >> "$GITHUB_OUTPUT" + - name: Set matrix + id: set-matrix + run: | + release_version="${{ steps.event.outputs.tag }}" + repo_name="${{ steps.registry-repo.outputs.repo_name }}" + release_info=$(curl -s "https://api.github.com/repos/${{ github.repository }}/releases/tags/v${release_version}") asset_prefix="${repo_name}-${release_version}-" - oci_images=$(jq -c --arg asset_prefix "$asset_prefix" '[.release.assets[].name | select(endswith(".oci")) | split(".oci") | .[0] | split($asset_prefix) | .[1]]' "${GITHUB_EVENT_PATH}") + oci_images=$(echo $release_info | jq -c --arg asset_prefix "$asset_prefix" '[ .assets[] | select(.name | endswith(".oci")) | {name: (.name | split(".oci") | .[0] | split($asset_prefix) | .[1]), url}]') + printf "matrix=%s\n" "${oci_images}" printf "matrix=%s\n" "${oci_images}" >> "$GITHUB_OUTPUT" - name: Set DOCKERHUB_ORG namespace id: set-dockerhub-org-namespace - run: echo "DOCKERHUB_ORG=${GITHUB_REPOSITORY_OWNER//-/}" >> "$GITHUB_OUTPUT" + run: | + echo "DOCKERHUB_ORG=${GITHUB_REPOSITORY_OWNER//-/}" >> "$GITHUB_OUTPUT" - name: Parse Configs id: parse_configs - run: | - registries_filename="${{ env.REGISTRIES_FILENAME }}" + registries_filepath="${{ env.REGISTRIES_FILEPATH }}" push_to_dockerhub=true push_to_gcr=true - if [[ -f $registries_filename ]]; then - if jq 'has("dockerhub")' $registries_filename > /dev/null; then - push_to_dockerhub=$(jq '.dockerhub' $registries_filename) + if [[ -f $registries_filepath ]]; then + + if jq 'has("dockerhub")' $registries_filepath > /dev/null; then + push_to_dockerhub=$(jq '.dockerhub' $registries_filepath) fi - if jq 'has("GCR")' $registries_filename > /dev/null; then - push_to_gcr=$(jq '.GCR' $registries_filename) + + if jq 'has("GCR")' $registries_filepath > /dev/null; then + push_to_gcr=$(jq '.GCR' $registries_filepath) fi fi @@ -68,50 +99,66 @@ jobs: oci_image: ${{ fromJSON(needs.preparation.outputs.matrix) }} steps: - - name: Parse Event - id: event - run: | - echo "tag=$(jq -r '.release.tag_name' "${GITHUB_EVENT_PATH}" | sed s/^v//)" >> "$GITHUB_OUTPUT" - echo "${{ matrix.oci_image }}_download_url=$(jq -r '.release.assets[] | select(.name | endswith("${{ matrix.oci_image }}.oci")) | .url' "${GITHUB_EVENT_PATH}")" >> "$GITHUB_OUTPUT" - - name: Checkout uses: actions/checkout@v4 - - name: Download ${{ matrix.oci_image }} Image + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up buildx + uses: docker/setup-buildx-action@v3 + + - name: Download ${{ matrix.oci_image.name }} Image uses: paketo-buildpacks/github-config/actions/release/download-asset@main with: - url: ${{ steps.event.outputs[format('{0}_download_url', matrix.oci_image)] }} - output: "/github/workspace/${{ matrix.oci_image }}.oci" + url: "${{ matrix.oci_image.url }}" + output: "./${{ matrix.oci_image.name }}.oci" token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} - - name: Get Registry Repo Name - id: registry-repo - run: | - # Strip off the Github org prefix and 'stack' suffix from repo name - # paketo-buildpacks/some-name-stack --> some-name - echo "name=$(echo "${{ github.repository }}" | sed 's/^.*\///' | sed 's/\-stack$//')" >> "$GITHUB_OUTPUT" + - name: Docker login docker.io + uses: docker/login-action@v3 + if: ${{ needs.preparation.outputs.push_to_dockerhub == 'true' }} + with: + username: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_USERNAME }} + password: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD }} + registry: docker.io + + - name: Docker login gcr.io + uses: docker/login-action@v3 + if: ${{ needs.preparation.outputs.push_to_gcr == 'true' }} + with: + username: ${{ env.GCR_USERNAME }} + password: ${{ secrets.GCR_PUSH_BOT_JSON_KEY }} + registry: ${{ env.GCR_REGISTRY }} - - name: Push ${{ matrix.oci_image }} Image to DockerHub + - name: Push ${{ matrix.oci_image.name }} Image to registries id: push env: - DOCKERHUB_USERNAME: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_USERNAME }} - DOCKERHUB_PASSWORD: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD }} DOCKERHUB_ORG: "${{ needs.preparation.outputs.DOCKERHUB_ORG }}" - GCR_USERNAME: _json_key - GCR_PASSWORD: ${{ secrets.GCR_PUSH_BOT_JSON_KEY }} GCR_PROJECT: "${{ github.repository_owner }}" run: | - - if [ "${{ needs.preparation.outputs.push_to_dockerhub }}" == "true" ]; then - echo "${DOCKERHUB_PASSWORD}" | sudo skopeo login --username "${DOCKERHUB_USERNAME}" --password-stdin index.docker.io - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image }}-${{ steps.registry-repo.outputs.name }}:${{ steps.event.outputs.tag }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image }}-${{ steps.registry-repo.outputs.name }}:latest" - fi - - if [ "${{ needs.preparation.outputs.push_to_gcr }}" == "true" ]; then - echo "${GCR_PASSWORD}" | sudo skopeo login --username "${GCR_USERNAME}" --password-stdin gcr.io - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://gcr.io/${GCR_PROJECT}/${{ matrix.oci_image }}-${{ steps.registry-repo.outputs.name }}:${{ steps.event.outputs.tag }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://gcr.io/${GCR_PROJECT}/${{ matrix.oci_image }}-${{ steps.registry-repo.outputs.name }}:latest" + # Ensure other scripts can access the .bin directory to install their own + # tools after we install them as whatever user we are. + mkdir -p ./.bin/ + chmod 777 ./.bin/ + + ./scripts/publish.sh \ + --image-ref "docker.io/${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}-${{ needs.preparation.outputs.repo_name }}:${{ needs.preparation.outputs.tag }}" \ + --image-ref "docker.io/${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}-${{ needs.preparation.outputs.repo_name }}:latest" \ + --image-archive "./${{ matrix.oci_image.name }}.oci" + + if [ "${{ needs.preparation.outputs.push_to_gcr }}" = "true" ]; then + + platforms=$(docker manifest inspect "docker.io/${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}-${{ needs.preparation.outputs.repo_name }}:${{ needs.preparation.outputs.tag }}" | + jq -r '[.manifests[].platform] | [.[] | .os + "/" + .architecture] | join(",")') + + echo "FROM docker.io/${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}-${{ needs.preparation.outputs.repo_name }}:${{ needs.preparation.outputs.tag }}" | \ + docker buildx build -f - . \ + --tag "${{ env.GCR_REGISTRY }}/${GCR_PROJECT}/${{ matrix.oci_image.name }}-${{ needs.preparation.outputs.repo_name }}:${{ needs.preparation.outputs.tag }}" \ + --tag "${{ env.GCR_REGISTRY }}/${GCR_PROJECT}/${{ matrix.oci_image.name }}-${{ needs.preparation.outputs.repo_name }}:latest" \ + --platform "$platforms" \ + --provenance=false \ + --push fi # If the repository name contains 'bionic', let's push it to legacy image locations as well: # paketobuildpacks/{build/run}:{version}-{variant} @@ -125,12 +172,12 @@ jobs: # bionic-tiny --> tiny variant="${registry_repo#bionic-}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image }}:${{ steps.event.outputs.tag }}-${variant}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image }}:${{ steps.event.outputs.tag }}-${variant}-cnb" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image }}:${variant}-cnb" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/${{ matrix.oci_image }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image }}:${variant}" + sudo skopeo copy "oci-archive:./${{ matrix.oci_image.name }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}:${{ steps.event.outputs.tag }}-${variant}" + sudo skopeo copy "oci-archive:./${{ matrix.oci_image.name }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}:${{ steps.event.outputs.tag }}-${variant}-cnb" + sudo skopeo copy "oci-archive:./${{ matrix.oci_image.name }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}:${variant}-cnb" + sudo skopeo copy "oci-archive:./${{ matrix.oci_image.name }}.oci" "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}:${variant}" - sudo skopeo copy "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image }}:${variant}-cnb" "docker://gcr.io/${GCR_PROJECT}/${{ matrix.oci_image }}:${variant}-cnb" + sudo skopeo copy "docker://${DOCKERHUB_ORG}/${{ matrix.oci_image.name }}:${variant}-cnb" "docker://gcr.io/${GCR_PROJECT}/${{ matrix.oci_image.name }}:${variant}-cnb" fi failure: diff --git a/.github/workflows/test-pull-request.yml b/.github/workflows/test-pull-request.yml index d13cc82..22c3e63 100644 --- a/.github/workflows/test-pull-request.yml +++ b/.github/workflows/test-pull-request.yml @@ -18,6 +18,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Create stack id: create-stack run: | diff --git a/buildpack_integration_test.go b/buildpack_integration_test.go index 8e66d51..76fe020 100644 --- a/buildpack_integration_test.go +++ b/buildpack_integration_test.go @@ -67,7 +67,11 @@ func testBuildpackIntegration(t *testing.T, context spec.G, it spec.S) { } it("should successfully build a go app", func() { - _, runImageUrl, builderImageUrl, err = utils.GenerateBuilder(root, stack.OutputDir, RegistryUrl) + _, runImageUrl, builderImageUrl, err = utils.GenerateBuilder( + filepath.Join(root, DefaultStack.OutputDir, "build.oci"), + filepath.Join(root, stack.OutputDir, "run.oci"), + RegistryUrl, + ) Expect(err).NotTo(HaveOccurred()) image, _, err = pack.WithNoColor().Build. @@ -130,7 +134,11 @@ func testBuildpackIntegration(t *testing.T, context spec.G, it spec.S) { } it(fmt.Sprintf("it should successfully get the %s version of the run image", stack.Name), func() { - _, runImageUrl, builderImageUrl, err = utils.GenerateBuilder(root, stack.OutputDir, RegistryUrl) + _, runImageUrl, builderImageUrl, err = utils.GenerateBuilder( + filepath.Join(root, DefaultStack.OutputDir, "build.oci"), + filepath.Join(root, stack.OutputDir, "run.oci"), + RegistryUrl, + ) Expect(err).NotTo(HaveOccurred()) image, _, err = pack.WithNoColor().Build. diff --git a/init_test.go b/init_test.go index 3783148..6540639 100644 --- a/init_test.go +++ b/init_test.go @@ -144,8 +144,8 @@ func TestAcceptance(t *testing.T) { Expect(err).NotTo(HaveOccurred()) builder.buildImageUrl, builder.runImageUrl, builder.imageUrl, err = utils.GenerateBuilder( - root, - DefaultStack.OutputDir, + filepath.Join(root, DefaultStack.OutputDir, "build.oci"), + filepath.Join(root, DefaultStack.OutputDir, "run.oci"), RegistryUrl, ) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 30d383f..24f7724 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "os" - "path/filepath" "github.com/google/uuid" @@ -13,18 +12,16 @@ import ( "github.com/paketo-buildpacks/packit/v2/pexec" ) -func GenerateBuilder(rootDir string, outputDir string, registryUrl string) (buildImageUrl string, runImageUrl string, builderImageUrl string, err error) { +func GenerateBuilder(buildImage string, runImage string, registryUrl string) (buildImageUrl string, runImageUrl string, builderImageUrl string, err error) { - buildArchive := filepath.Join(rootDir, outputDir, "build.oci") buildImageID := fmt.Sprintf("build-image-%s", uuid.NewString()) - buildImageUrl, err = PushFileToLocalRegistry(buildArchive, registryUrl, buildImageID) + buildImageUrl, err = PushFileToLocalRegistry(buildImage, registryUrl, buildImageID) if err != nil { return "", "", "", err } - runArchive := filepath.Join(rootDir, outputDir, "run.oci") runImageID := fmt.Sprintf("run-image-%s", uuid.NewString()) - runImageUrl, err = PushFileToLocalRegistry(runArchive, registryUrl, runImageID) + runImageUrl, err = PushFileToLocalRegistry(runImage, registryUrl, runImageID) if err != nil { return "", "", "", err } @@ -80,19 +77,17 @@ func GenerateBuilder(rootDir string, outputDir string, registryUrl string) (buil func PushFileToLocalRegistry(filePath string, registryUrl string, imageName string) (string, error) { buf := bytes.NewBuffer(nil) - imageURL := fmt.Sprintf("%s/%s", registryUrl, imageName) - skopeo := pexec.NewExecutable("skopeo") - - err := skopeo.Execute(pexec.Execution{ + jam := pexec.NewExecutable("jam") + err := jam.Execute(pexec.Execution{ Stdout: buf, Stderr: buf, Args: []string{ - "copy", - fmt.Sprintf("oci-archive:%s", filePath), - fmt.Sprintf("docker://%s:latest", imageURL), - "--dest-tls-verify=false", + "publish-image", + "--image-ref", + imageURL, + "--image-archive", filePath, }, }) diff --git a/metadata_test.go b/metadata_test.go index eef4503..c4cdf7f 100644 --- a/metadata_test.go +++ b/metadata_test.go @@ -51,7 +51,7 @@ func testMetadata(t *testing.T, context spec.G, it spec.S) { index, manifests, err := getImageIndexAndManifests(tmpDir, filepath.Join(root, imageInfo.OutputDir, "build.oci")) Expect(err).NotTo(HaveOccurred()) - Expect(manifests).To(HaveLen(1)) + Expect(manifests).To(HaveLen(4)) Expect(manifests[0].Platform).To(Equal(&v1.Platform{ OS: "linux", Architecture: "amd64", @@ -99,7 +99,7 @@ func testMetadata(t *testing.T, context spec.G, it spec.S) { index, manifests, err := getImageIndexAndManifests(tmpDir, filepath.Join(root, imageInfo.OutputDir, "run.oci")) Expect(err).NotTo(HaveOccurred()) - Expect(manifests).To(HaveLen(1)) + Expect(manifests).To(HaveLen(4)) Expect(manifests[0].Platform).To(Equal(&v1.Platform{ OS: "linux", Architecture: "amd64", diff --git a/scripts/.syncignore b/scripts/.syncignore index a97b728..117cff5 100644 --- a/scripts/.syncignore +++ b/scripts/.syncignore @@ -1,2 +1,4 @@ test.sh -options.json \ No newline at end of file +options.json +receipts.sh +publish.sh \ No newline at end of file diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100755 index 0000000..44481a5 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +readonly PROG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly ROOT_DIR="$(cd "${PROG_DIR}/.." && pwd)" +readonly BIN_DIR="${ROOT_DIR}/.bin" + +# shellcheck source=SCRIPTDIR/.util/tools.sh +source "${PROG_DIR}/.util/tools.sh" + +# shellcheck source=SCRIPTDIR/.util/print.sh +source "${PROG_DIR}/.util/print.sh" + +if [[ $BASH_VERSINFO -lt 4 ]]; then + util::print::error "Before running this script please update Bash to v4 or higher (e.g. on OSX: \$ brew install bash)" +fi + +function main() { + local build_ref=() + local run_ref=() + local image_ref=() + local build_archive="" + local run_archive="" + local image_archive="" + + while [[ "${#}" != 0 ]]; do + case "${1}" in + --help|-h) + shift 1 + usage + exit 0 + ;; + + --build-ref) + build_ref+=("${2}") + shift 2 + ;; + + --run-ref) + run_ref+=("${2}") + shift 2 + ;; + + --build-archive) + build_archive=${2} + shift 2 + ;; + + --run-archive) + run_archive=${2} + shift 2 + ;; + + --image-ref) + image_ref+=("${2}") + shift 2 + ;; + + --image-archive) + image_archive=${2} + shift 2 + ;; + + "") + # skip if the argument is empty + shift 1 + ;; + + *) + util::print::error "unknown argument \"${1}\"" + esac + done + + if [[ ${#image_ref[@]} != 0 || -n "$image_archive" ]]; then + if ((${#image_ref[@]} == 0)); then + util::print::error "--image-ref is required [Example: docker.io/paketobuildpacks/foo:latest]" + fi + + if [ -z "$image_archive" ]; then + util::print::error "--image-archive is required [Example: ./path/to/image.oci]" + fi + else + if ((${#build_ref[@]} == 0)); then + util::print::error "--build-ref is required [Example: docker.io/paketobuildpacks/foo:latest]" + fi + + if ((${#run_ref[@]} == 0)); then + util::print::error "--run-ref is required [Example: gcr.iopaketo-buildpacks/foo:1.0.0]" + fi + + if ((${#run_ref[@]} != ${#build_ref[@]})); then + util::print::error "must have the same number of --build-ref and --run-ref arguments" + fi + + if [ -z "$build_archive" ]; then + util::print::error "--build-archive is required [Example: ./path/to/build.oci]" + fi + + if [ -z "$run_archive" ]; then + util::print::error "--run-archive is required [Example: ./path/to/run.oci]" + fi + fi + + tools::install + + if [[ ${#image_ref[@]} != 0 || -n "$image_archive" ]]; then + stack::publish::image \ + "$image_archive" \ + "${#image_ref[@]}" \ + "${image_ref[@]}" + else + stack::publish \ + "$build_archive" \ + "$run_archive" \ + "${#build_ref[@]}" \ + "${build_ref[@]}" \ + "${#run_ref[@]}" \ + "${run_ref[@]}" + fi +} + +function usage() { + cat <<-USAGE +publish.sh [OPTIONS] + +Publishes the stack using the existing OCI image archives. + +OPTIONS + --build-ref list of build references to publish to [Required if --image-ref is not provided] + --run-ref list of run references to publish to [Required if --image-ref is not provided] + --build-archive path to the build OCI archive file [Required if --image-ref is not provided] + --run-archive path to the run OCI archive file [Required if --image-ref is not provided] + --image-ref list of image references to publish to [Required if --build-ref and --run-ref are not provided] + --image-archive path to the image OCI archive file [Required if --build-ref and --run-ref are not provided] + --help -h prints the command usage +USAGE +} + +function tools::install() { + util::tools::jam::install \ + --directory "${BIN_DIR}" +} + +function stack::publish() { + local build_archive="$1" + local run_archive="$2" + + # bash can't easily pass arrays, they all get merged into one list of arguments + # so we pass the lengths & extract the arrays from the single argument list + local build_ref_len="$3" # length of build ref array + local build_ref=("${@:4:$build_ref_len}") # pull out build_ref array + local run_len_slot=$(( 4 + build_ref_len)) # location of run_ref length + local run_ref_len="${*:$run_len_slot:1}" # length of run ref array + local run_ref_slot=$(( 1 + run_len_slot)) # location of run_ref array + local run_ref=("${@:$run_ref_slot:$run_ref_len}") # pull out run_ref array + + # iterate over build_ref & run_ref, they will be the same length + local len=${#build_ref[@]} + for (( i=0; i/dev/null + + # Ensure we can write to the build_dir + if [ $(stat -c %u $build_dir) = "0" ]; then + sudo chown -R "$(id -u):$(id -g)" "$build_dir" + fi + + for archivePath in "${buildArchive}" "${runArchive}" ; do + archiveName=$(basename "${archivePath}") # either 'build.oci' or 'run.oci' + imageType=$(basename -s .oci "${archivePath}") # either 'build' or 'run' + + util::print::title "Generating package SBOM for ${archiveName}" + + for imageArch in $(crane manifest "$localRegistry/$imageType" | jq -r '.manifests[].platform.architecture'); do + + if [[ "$imageType" = "build" ]]; then + dir=$(dirname ${buildOutput}) + fileName=$(basename ${buildOutput}) + elif [[ "$imageType" = "run" ]]; then + dir=$(dirname ${runOutput}) + fileName=$(basename ${runOutput}) + fi + + if [ $imageArch = "amd64" ]; then + imageReceipt="${dir}/${fileName}" + else + imageReceipt="${dir}/${imageArch}-${fileName}" + fi + + util::print::info "Generating CycloneDX package SBOM using syft for $archiveName on platform linux/$imageArch saved as $imageReceipt" + + # Generate the architecture-specific SBOM from image in the local registry + syft scan "registry:$localRegistry/$imageType" \ + --output cyclonedx-json="$imageReceipt" \ + --platform "linux/$imageArch" + + receiptFilenames+="$imageReceipt\n" + done + done - util::print::info "Generating CycloneDX package SBOM using syft" - syft packages "${image}" --output cyclonedx-json --file "${output}" + kill $registryPid + echo $receiptFilenames } -main "${@:-}" +main "${@:-}" \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh index 7d263d9..2af7176 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -16,13 +16,16 @@ source "${PROG_DIR}/.util/tools.sh" source "${PROG_DIR}/.util/print.sh" function main() { - local clean token test_only_stacks registryPort registryPid localRegistry setupLocalRegistry + local clean token test_only_stacks validate_stack_builds + local registryPort registryPid localRegistry setupLocalRegistry + help="" clean="false" token="" test_only_stacks="" registryPid="" setupLocalRegistry="" + validate_stack_builds="false" while [[ "${#}" != 0 ]]; do case "${1}" in @@ -46,6 +49,11 @@ function main() { shift 2 ;; + --validate-stack-builds) + validate_stack_builds="true" + shift 1 + ;; + "") # skip if the argument is empty shift 1 @@ -100,8 +108,13 @@ function main() { stack_output_builds_exist=$(stack_builds_exist) - util::print::title "Creating stack..." + if [[ "${stack_output_builds_exist}" == "false" && "${validate_stack_builds}" == "true" ]]; then + util::print::error "Stack builds are not valid." + exit 1 + fi + if [[ "${stack_output_builds_exist}" == "false" ]]; then + util::print::title "Creating stack..." while read -r image; do config_dir=$(echo "${image}" | jq -r '.config_dir') output_dir=$(echo "${image}" | jq -r '.output_dir') @@ -109,6 +122,8 @@ function main() { --stack-dir "${config_dir}" \ --build-dir "${output_dir}" done <<<"$STACK_IMAGES" + else + util::print::title "Stack builds already exist..." fi if [[ -f $INTEGRATION_JSON ]]; then @@ -120,6 +135,7 @@ function main() { registryPid=$(local::registry::start $registryPort) localRegistry="127.0.0.1:$registryPort" export REGISTRY_URL="${localRegistry}" + util::print::info "Local registry is running with url: ${localRegistry}" fi tests::run @@ -163,10 +179,11 @@ ${joined_oci_images} if they exist. Otherwise, first runs create.sh to create them. OPTIONS - --clean -c clears contents of stack output directory before running tests - --token -t Token used to download assets from GitHub (e.g. jam, pack, etc) (optional) - --test-only-stacks Runs the tests of the stacks passed to this argument (e.g. java-8 nodejs-16) (optional) - --help -h prints the command usage + --clean -c Clears contents of stack output directory before running tests + --token -t Token used to download assets from GitHub (e.g. jam, pack, etc) (optional) + --test-only-stacks Runs the tests of the stacks passed to this argument (e.g. java-8 nodejs-16) (optional) + --validate-stack-builds Validates that the stack builds are present before running tests (optional) + --help -h Prints the command usage USAGE } @@ -193,6 +210,7 @@ function tests::run() { util::print::title "Run Stack Acceptance Tests" export CGO_ENABLED=0 + export JAM_PATH="${STACK_DIR}/.bin/jam" testout=$(mktemp) pushd "${STACK_DIR}" > /dev/null if GOMAXPROCS="${GOMAXPROCS:-4}" go test -count=1 -timeout 0 ./... -v -run Acceptance | tee "${testout}"; then @@ -210,7 +228,12 @@ function stack_builds_exist() { while IFS= read -r image; do stack_output_dir=$(echo "${image}" | jq -r '.output_dir') - if ! [[ -f "${STACK_DIR}/${stack_output_dir}/build.oci" ]] || ! [[ -f "${STACK_DIR}/${stack_output_dir}/run.oci" ]]; then + is_build_image_necessary=$(echo "${image}" | jq -r '.create_build_image // false') + + if ! [[ -f "${STACK_DIR}/${stack_output_dir}/run.oci" ]]; then + stack_output_builds_exist="false" + fi + if [[ ! -f "${STACK_DIR}/${stack_output_dir}/build.oci" && "${is_build_image_necessary}" == true ]]; then stack_output_builds_exist="false" fi done <<<"$STACK_IMAGES" diff --git a/stacks/stack-java-11/stack.toml b/stacks/stack-java-11/stack.toml index 9db3407..a087ca6 100644 --- a/stacks/stack-java-11/stack.toml +++ b/stacks/stack-java-11/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks" diff --git a/stacks/stack-java-17/stack.toml b/stacks/stack-java-17/stack.toml index 3be61a3..de0b462 100644 --- a/stacks/stack-java-17/stack.toml +++ b/stacks/stack-java-17/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks" diff --git a/stacks/stack-java-21/stack.toml b/stacks/stack-java-21/stack.toml index e29b9ad..45f7693 100644 --- a/stacks/stack-java-21/stack.toml +++ b/stacks/stack-java-21/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks" diff --git a/stacks/stack-java-8/stack.toml b/stacks/stack-java-8/stack.toml index de86e25..f544d27 100644 --- a/stacks/stack-java-8/stack.toml +++ b/stacks/stack-java-8/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks" diff --git a/stacks/stack-nodejs-16/stack.toml b/stacks/stack-nodejs-16/stack.toml index cf59745..fb938fc 100644 --- a/stacks/stack-nodejs-16/stack.toml +++ b/stacks/stack-nodejs-16/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks" diff --git a/stacks/stack-nodejs-18/stack.toml b/stacks/stack-nodejs-18/stack.toml index 972b765..52d9736 100644 --- a/stacks/stack-nodejs-18/stack.toml +++ b/stacks/stack-nodejs-18/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks" diff --git a/stacks/stack-nodejs-20/stack.toml b/stacks/stack-nodejs-20/stack.toml index da0c7a3..772aca0 100644 --- a/stacks/stack-nodejs-20/stack.toml +++ b/stacks/stack-nodejs-20/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks" diff --git a/stacks/stack/stack.toml b/stacks/stack/stack.toml index 1ab329d..dd3f0e5 100644 --- a/stacks/stack/stack.toml +++ b/stacks/stack/stack.toml @@ -2,7 +2,7 @@ id = "io.buildpacks.stacks.ubi8" homepage = "https://github.com/paketo-community/ubi-base-stack" maintainer = "Paketo Community" -platforms = ["linux/amd64"] +platforms = ["linux/amd64", "linux/arm64", "linux/ppc64le", "linux/s390x"] [build] description = "base build ubi8 image to support buildpacks"