Skip to content

Create Release

Create Release #18945

name: Create Release
on:
schedule:
- cron: '*/30 * * * *' # every 30 minutes
push:
branches:
- main
workflow_dispatch:
inputs:
version:
description: 'Version of the release to cut (e.g. 1.2.3). No leading v'
required: false
force:
description: 'Release stack even if change validator does not detect changes, or a package is removed'
required: true
type: choice
default: 'false'
options:
- 'true'
- 'false'
concurrency: release
env:
STACKS_FILEPATH: "stacks/images.json"
PATCHED_USNS_FILENAME: "patched-usns.json"
jobs:
preparation:
name: Preparation
runs-on: ubuntu-22.04
outputs:
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 stacks images
id: get-stacks
run: |
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 <<EOF
[
{
"name": "default",
"config_dir": "stack",
"output_dir": "build",
"build_image": "build",
"build_receipt_filename": "build-receipt.cyclonedx.json",
"run_image": "run",
"run_receipt_filename": "run-receipt.cyclonedx.json",
"create_build_image": true,
"is_new": false
}
]
EOF
)
stack_files_dir="stack"
fi
echo "stack_files_dir=$stack_files_dir" >> "$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_FILEPATH }} ]]; then
support_usns=$( jq '.support_usns' ${{ env.STACKS_FILEPATH }} )
fi
if [ $support_usns == true ]; then
echo "polling_type=usn" >> "$GITHUB_OUTPUT"
else
echo "polling_type=hash" >> "$GITHUB_OUTPUT"
fi
echo "support_usns=${support_usns}" >> "$GITHUB_OUTPUT"
- name: Get Repository Name
id: repo
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"
# translates 'paketo-buildpacks' to 'paketobuildpacks'
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 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: "${{ steps.hashcode_pattern.outputs.build }}"
search_depth: 1
repo: ${{ github.repository }}
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: "${{ steps.hashcode_pattern.outputs.run }}"
search_depth: 1
repo: ${{ github.repository }}
output_path: "./previous_${{ matrix.arch.name }}-${{ matrix.stacks.run_image }}.oci.sha256"
token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
- 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_build_container_image }} > ./hash-code-current-build-image-${{ matrix.arch.name }}-${{ matrix.stacks.name }}
- 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_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.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.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.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.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: [ 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:
- name: Checkout With History
uses: actions/checkout@v4
with:
fetch-depth: 0 # gets full history
- name: Compare With Previous Release
id: compare
run: |
# shellcheck disable=SC2046
changed=$(git diff --name-only $(git describe --tags --abbrev=0) -- "${{ needs.preparation.outputs.stack_files_dir }}")
if [ -z "${changed}" ]
then
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
run_if_stack_files_changed:
name: Run If Stack Files Changed
runs-on: ubuntu-22.04
needs: [stack_files_changed]
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: [ 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:
stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }}
steps:
- 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: |
scripts/create.sh --stack-dir ${{ matrix.stacks.config_dir }} \
--build-dir ${{ matrix.stacks.output_dir }}
- name: Generate Package Receipts
id: receipts
run: |
scripts/receipts.sh --build-image "${{ matrix.stacks.output_dir }}/build.oci" \
--run-image "${{ matrix.stacks.output_dir }}/run.oci" \
--build-receipt current-build-receipt-${{ matrix.stacks.name }} \
--run-receipt current-run-receipt-${{ matrix.stacks.name }}
- 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
- name: Upload build image
if: ${{ matrix.stacks.create_build_image == true }}
uses: actions/upload-artifact@v4
with:
name: current-build-image-${{ matrix.stacks.name }}
path: "${{ matrix.stacks.output_dir }}/build.oci"
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: "*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: "*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 ]
runs-on: ubuntu-22.04
strategy:
matrix:
stacks: ${{ fromJSON(needs.preparation.outputs.stacks) }}
arch: ${{ fromJSON(needs.preparation.outputs.architectures) }}
steps:
- name: Checkout With History
uses: actions/checkout@v4
with:
fetch-depth: 0 # gets full history
- name: Download Current Receipt(s)
uses: actions/download-artifact@v4
with:
pattern: current-*-receipt-${{ matrix.stacks.name }}
path: receipt-files
- 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
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: "${{ steps.previous_receipt_download_pattern.outputs.build_filename }}"
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 }}
- name: Find and 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: "${{ steps.previous_receipt_download_pattern.outputs.run_filename }}"
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 }}
- 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/${{ 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/${{ 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
run: |
if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then
build=$(jq '. | length' <<< "${BUILD_REMOVED}")
echo "Build packages removed: ${build}"
fi
run=$(jq '. | length' <<< "${RUN_REMOVED}")
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 [ "${{ 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 }}
RUN_REMOVED: ${{ steps.run_diff.outputs.removed }}
- name: Create/Upload variable artifacts
id: variable_artifacts
run: |
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
echo '${{ steps.build_diff.outputs.modified }}' > build_modified
echo '${{ steps.build_diff.outputs.removed }}' > build_removed_with_force
fi
echo '${{ steps.run_diff.outputs.added }}' > run_added
echo '${{ steps.run_diff.outputs.modified }}' > run_modified
echo '${{ steps.run_diff.outputs.removed }}' > run_removed_with_force
- 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: "${{ 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
needs: [ diff ]
runs-on: ubuntu-22.04
if: ${{ needs.diff.outputs.removed_with_force == 'true' }}
steps:
- name: Run if packages removed with force
run: |
echo "packages removed with user-provided force"
packages_changed:
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.arch.name }}-${{ matrix.stacks.name }}
uses: actions/download-artifact@v4
with:
name: diff-${{ matrix.arch.name }}-${{ matrix.stacks.name }}
- name: Set env diff variables
run: |
if [ "${{ matrix.stacks.create_build_image }}" == "true" ]; then
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
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
build_added=$(cat ./build_added | jq 'length')
echo "Build packages added: ${build_added}"
# shellcheck disable=SC2153
build_modified=$(cat ./build_modified | jq 'length')
echo "Build packages modified: ${build_modified}"
fi
# shellcheck disable=SC2153
run_added=$(jq '. | length' <<< "${RUN_ADDED}")
echo "Run packages added: ${run_added}"
# shellcheck disable=SC2153
run_modified=$(jq '. | length' <<< "${RUN_MODIFIED}")
echo "Run packages modified: ${run_modified}"
if [ "${build_added}" -eq 0 ] && [ "${build_modified}" -eq 0 ] && [ "${run_added}" -eq 0 ] && [ "${run_modified}" -eq 0 ]; then
echo "No packages changed."
# 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
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' && !cancelled() }}
steps:
- name: Run if packages changed
run: |
echo "packages have changed"
test:
name: Acceptance Test
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
- name: Checkout
uses: actions/checkout@v4
- name: Download Build Image(s)
uses: actions/download-artifact@v4
with:
pattern: current-build-image-*
- name: Download Run Image(s)
uses: actions/download-artifact@v4
with:
pattern: current-run-image-*
- name: Create OCI artifacts destination directory
run: |
echo '${{ needs.preparation.outputs.stacks }}' | jq -c '.[]' | while read -r stack; do
name=$(echo "$stack" | jq -r '.name')
output_dir=$(echo "$stack" | jq -r '.output_dir')
create_build_image=$(echo "$stack" | jq -r '.create_build_image')
mkdir -p $output_dir
mv "current-run-image-${name}/run.oci" "${output_dir}/run.oci"
if [ $create_build_image == 'true' ]; then
mv "current-build-image-${name}/build.oci" "${output_dir}/build.oci"
fi
done
- name: Run Acceptance Tests
run: ./scripts/test.sh --validate-stack-builds
force_release_creation:
name: Force Release Creation
runs-on: ubuntu-22.04
if: ${{github.event.inputs.force == 'true'}}
steps:
- name: Signal force release creation
run: |
echo "Force release creation input set to true"
release:
name: Release
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' ) }}
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- name: Print Release Reasoning
run: |
printf "Diff Packages: %s\n" "${{ needs.diff.result }}"
printf "Acceptance Tests: %s\n" "${{ needs.test.result }}"
printf "Run If Packages Changed: %s\n" "${{ needs.run_if_packages_changed.result }}"
printf "Run If Packages Removed With Force: %s\n" "${{ needs.run_if_packages_removed_with_force.result }}"
printf "Run If Stack Files Changed: %s\n" "${{ needs.run_if_stack_files_changed.result }}"
printf "Force Release: %s\n" "${{ github.event.inputs.force }}"
- name: Checkout With History
uses: actions/checkout@v4
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
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: Setup Release Assets
id: assets
run: |
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
## Adding the SBOM files to the assets
for stack in $stacks; do
for arch in $archs; do
arch_name=$(echo "$arch" | jq -r '.name')
stack_name=$(echo "$stack" | jq -r '.name')
run_receipt_filename=$(echo "$stack" | jq -r '.run_receipt_filename')
receipt_file_name=$(
echo '[ "'"$arch_name"'" ,"current-run-receipt" , "'"$stack_name"'" ]' |
jq -r 'map(select(. != "amd64")) | join("-")'
)
receipt_asset_name=$(
echo '[ "'"$repo"'", "'"$tag"'", "'"$arch_name"'", "'"$run_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}" \
'. += [
{
"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
## 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
# 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
# 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
echo "assets=${assets}" >> "$GITHUB_OUTPUT"
- name: Generate Release Notes Description
id: notes
run: |
release_notes_dir="release-notes"
# 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
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${{ steps.tag.outputs.tag }}
target_commitish: ${{ github.sha }}
name: v${{ steps.tag.outputs.tag }}
body_filepath: ${{ steps.notes.outputs.release_body }}
draft: false
assets: ${{ steps.assets.outputs.assets }}
failure:
name: Alert on Failure
runs-on: ubuntu-22.04
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
with:
token: ${{ secrets.GITHUB_TOKEN }}
repo: ${{ github.repository }}
label: "failure:release"
comment_if_exists: true
issue_title: "Failure: Create Release workflow"
issue_body: |
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}}