Create Release #31239
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Create Release | |
on: | |
schedule: | |
- cron: '27 2,14 * * *' # daily at 02:27 and 14:27 UTC | |
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: | |
BUILD_RECEIPT_FILENAME: "build-receipt.cyclonedx.json" | |
RUN_RECEIPT_FILENAME: "run-receipt.cyclonedx.json" | |
PATCHED_USNS_FILENAME: "patched-usns.json" | |
jobs: | |
poll_usns: | |
name: Poll USNs | |
runs-on: ubuntu-24.04 | |
outputs: | |
usns: ${{ steps.usns.outputs.usns }} | |
steps: | |
- name: Find and Download Previous Build Receipt | |
id: previous_build | |
uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main | |
with: | |
asset_pattern: "${{ env.BUILD_RECEIPT_FILENAME }}" | |
search_depth: 1 | |
repo: ${{ github.repository }} | |
output_path: "/github/workspace/${{ env.BUILD_RECEIPT_FILENAME }}" | |
token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} | |
- name: Find and Download Previous Run Receipt | |
id: previous_run | |
uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main | |
with: | |
asset_pattern: "${{ env.RUN_RECEIPT_FILENAME }}" | |
search_depth: 1 | |
repo: ${{ github.repository }} | |
output_path: "/github/workspace/${{ env.RUN_RECEIPT_FILENAME }}" | |
token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} | |
- 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 }}/${{ env.BUILD_RECEIPT_FILENAME }}" | |
run_receipt: "${{ github.workspace }}/${{ env.RUN_RECEIPT_FILENAME }}" | |
- 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: "${{ env.PATCHED_USNS_FILENAME }}" | |
search_depth: "-1" # Search all releases | |
repo: ${{ github.repository }} | |
output_path: "/github/workspace/${{ env.PATCHED_USNS_FILENAME }}" | |
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}/${{ env.PATCHED_USNS_FILENAME }}") | |
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 }} | |
stack_files_changed: | |
name: Determine If Stack Files Changed | |
runs-on: ubuntu-24.04 | |
needs: poll_usns | |
if: ${{ ! ( needs.poll_usns.outputs.usns == '[]' && github.event_name == 'schedule' ) }} | |
outputs: | |
stack_files_changed: ${{ steps.compare.outputs.stack_files_changed }} | |
steps: | |
- name: Checkout With History | |
uses: actions/checkout@v3 | |
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) -- stack)" | |
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-24.04 | |
needs: [stack_files_changed] | |
if: ${{ needs.stack_files_changed.outputs.stack_files_changed == 'true' }} | |
steps: | |
- name: Run if stack files changed | |
run: | | |
echo "stack files have changed" | |
create_stack: | |
name: Create Stack | |
needs: poll_usns | |
if: ${{ ! ( needs.poll_usns.outputs.usns == '[]' && github.event_name == 'schedule' ) }} | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v3 | |
- name: Create stack | |
id: create-stack | |
run: | | |
scripts/create.sh | |
- name: Generate Package Receipts | |
id: receipts | |
run: | | |
scripts/receipts.sh --build-image "${GITHUB_WORKSPACE}/build/build.oci" \ | |
--run-image "${GITHUB_WORKSPACE}/build/run.oci" \ | |
--build-receipt ${{ env.BUILD_RECEIPT_FILENAME }} \ | |
--run-receipt ${{ env.RUN_RECEIPT_FILENAME }} | |
echo "build_receipt=${{ env.BUILD_RECEIPT_FILENAME }}" >> "$GITHUB_OUTPUT" | |
echo "run_receipt=${{ env.RUN_RECEIPT_FILENAME }}" >> "$GITHUB_OUTPUT" | |
- name: Upload run image | |
uses: actions/upload-artifact@v3 | |
with: | |
name: current-run-image | |
path: build/run.oci | |
- name: Upload build image | |
uses: actions/upload-artifact@v3 | |
with: | |
name: current-build-image | |
path: build/build.oci | |
- name: Upload Build receipt | |
uses: actions/upload-artifact@v3 | |
with: | |
name: current-build-receipt | |
path: ${{ steps.receipts.outputs.build_receipt }} | |
- name: Upload Run receipt | |
uses: actions/upload-artifact@v3 | |
with: | |
name: current-run-receipt | |
path: ${{ steps.receipts.outputs.run_receipt }} | |
diff: | |
name: Diff Packages | |
outputs: | |
build_added: ${{ steps.build_diff.outputs.added }} | |
build_modified: ${{ steps.build_diff.outputs.modified }} | |
build_removed_with_force: ${{ steps.build_diff.outputs.removed }} | |
run_added: ${{ steps.run_diff.outputs.added }} | |
run_modified: ${{ steps.run_diff.outputs.modified }} | |
run_removed_with_force: ${{ steps.run_diff.outputs.removed }} | |
removed_with_force: ${{ steps.removed_with_force.outputs.packages_removed }} | |
needs: [ create_stack ] | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Download Build Receipt | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-build-receipt | |
- name: Download Run Receipt | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-run-receipt | |
- 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' }} | |
run: | | |
echo '{"components":[]}' > "${{ github.workspace }}/previous-${{ env.BUILD_RECEIPT_FILENAME }}" | |
echo '{"components":[]}' > "${{ github.workspace }}/previous-${{ env.RUN_RECEIPT_FILENAME }}" | |
- name: Find and Download Previous Build Receipt | |
if: ${{ steps.check_previous.outputs.exists == 'true' }} | |
uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main | |
with: | |
asset_pattern: "${{ env.BUILD_RECEIPT_FILENAME }}" | |
search_depth: 1 | |
repo: ${{ github.repository }} | |
output_path: "/github/workspace/previous-${{ env.BUILD_RECEIPT_FILENAME }}" | |
token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} | |
- name: Find and Download Previous Run Receipt | |
if: ${{ steps.check_previous.outputs.exists == 'true' }} | |
uses: paketo-buildpacks/github-config/actions/release/find-and-download-asset@main | |
with: | |
asset_pattern: "${{ env.RUN_RECEIPT_FILENAME }}" | |
search_depth: 1 | |
repo: ${{ github.repository }} | |
output_path: "/github/workspace/previous-${{ env.RUN_RECEIPT_FILENAME }}" | |
token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} | |
- name: Compare Build Packages | |
id: build_diff | |
uses: paketo-buildpacks/github-config/actions/stack/diff-package-receipts@main | |
with: | |
previous: "/github/workspace/previous-${{ env.BUILD_RECEIPT_FILENAME }}" | |
current: "/github/workspace/${{ env.BUILD_RECEIPT_FILENAME }}" | |
- name: Compare Run Packages | |
id: run_diff | |
uses: paketo-buildpacks/github-config/actions/stack/diff-package-receipts@main | |
with: | |
previous: "/github/workspace/previous-${{ env.RUN_RECEIPT_FILENAME }}" | |
current: "/github/workspace/${{ env.RUN_RECEIPT_FILENAME }}" | |
- name: Fail If Packages Removed | |
id: removed_with_force | |
run: | | |
build=$(jq '. | length' <<< "${BUILD_REMOVED}") | |
echo "Build packages removed: ${build}" | |
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 ] || [ "${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 }} | |
run_if_packages_removed_with_force: | |
name: Run If Packages Removed With Force | |
needs: [ diff ] | |
runs-on: ubuntu-24.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 ] | |
runs-on: ubuntu-24.04 | |
outputs: | |
packages_changed: ${{ steps.compare.outputs.packages_changed }} | |
steps: | |
- name: Compare With Previous Release | |
id: compare | |
run: | | |
# shellcheck disable=SC2153 | |
build_added=$(jq '. | length' <<< "${BUILD_ADDED}") | |
echo "Build packages added: ${build_added}" | |
# shellcheck disable=SC2153 | |
build_modified=$(jq '. | length' <<< "${BUILD_MODIFIED}") | |
echo "Build packages modified: ${build_modified}" | |
# 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." | |
echo "packages_changed=false" >> "$GITHUB_OUTPUT" | |
else | |
echo "Packages changed." | |
echo "packages_changed=true" >> "$GITHUB_OUTPUT" | |
fi | |
env: | |
BUILD_ADDED: ${{ needs.diff.outputs.build_added }} | |
BUILD_MODIFIED: ${{ needs.diff.outputs.build_modified }} | |
RUN_ADDED: ${{ needs.diff.outputs.run_added }} | |
RUN_MODIFIED: ${{ needs.diff.outputs.run_modified }} | |
run_if_packages_changed: | |
name: Run If Packages Changed | |
runs-on: ubuntu-24.04 | |
needs: [packages_changed] | |
if: ${{ needs.packages_changed.outputs.packages_changed == 'true' }} | |
steps: | |
- name: Run if packages changed | |
run: | | |
echo "packages have changed" | |
test: | |
name: Acceptance Test | |
needs: [ create_stack ] | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Setup Go | |
uses: actions/setup-go@v3 | |
with: | |
go-version: 'stable' | |
- name: Checkout | |
uses: actions/checkout@v3 | |
- name: Create OCI artifacts destination directory | |
run: | | |
mkdir -p build | |
- name: Download Build Image | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-build-image | |
path: build | |
- name: Download Run Image | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-run-image | |
path: build | |
- name: Run Acceptance Tests | |
run: ./scripts/test.sh | |
force_release_creation: | |
name: Force Release Creation | |
runs-on: ubuntu-24.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-24.04 | |
needs: [poll_usns, create_stack, diff, run_if_stack_files_changed, run_if_packages_changed, run_if_packages_removed_with_force, test, force_release_creation ] | |
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' ) }} | |
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@v3 | |
with: | |
fetch-depth: 0 # gets full history | |
- name: Download current build image | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-build-image | |
- name: Download current run image | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-run-image | |
- name: Download Build Receipt | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-build-receipt | |
- name: Download Run Receipt | |
uses: actions/download-artifact@v3 | |
with: | |
name: current-run-receipt | |
- 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: Write USNs File | |
id: write_usns | |
if: ${{ needs.poll_usns.outputs.usns != '[]' }} | |
run: | | |
jq . <<< "${USNS}" > "${USNS_PATH}" | |
echo "usns=${USNS_PATH}" >> "$GITHUB_OUTPUT" | |
env: | |
USNS_PATH: "${{ env.PATCHED_USNS_FILENAME }}" | |
USNS: ${{ needs.poll_usns.outputs.usns }} | |
- 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: Create Release Notes | |
id: notes | |
uses: paketo-buildpacks/github-config/actions/stack/release-notes@main | |
with: | |
build_image: "paketobuildpacks/build-${{ steps.repo_name.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}" | |
run_image: "paketobuildpacks/run-${{ steps.repo_name.outputs.registry_repo_name }}:${{ steps.tag.outputs.tag }}" | |
build_packages_added: ${{ needs.diff.outputs.build_added }} | |
build_packages_modified: ${{ needs.diff.outputs.build_modified }} | |
build_packages_removed_with_force: ${{ needs.diff.outputs.build_removed_with_force }} | |
run_packages_added: ${{ needs.diff.outputs.run_added }} | |
run_packages_modified: ${{ needs.diff.outputs.run_modified }} | |
run_packages_removed_with_force: ${{ needs.diff.outputs.run_removed_with_force }} | |
patched_usns: ${{ needs.poll_usns.outputs.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 "${{ env.BUILD_RECEIPT_FILENAME }}" \ | |
--arg run_receipt "${{ env.RUN_RECEIPT_FILENAME }}" \ | |
'[ | |
{ | |
"path": "build.oci", | |
"name": ($repo + "-" + $tag + "-" + "build.oci"), | |
"content_type": "application/gzip" | |
}, | |
{ | |
"path": "run.oci", | |
"name": ($repo + "-" + $tag + "-" + "run.oci"), | |
"content_type": "application/gzip" | |
}, | |
{ | |
"path": $build_receipt, | |
"name": ($repo + "-" + $tag + "-" + $build_receipt), | |
"content_type": "text/plain" | |
}, | |
{ | |
"path": $run_receipt, | |
"name": ($repo + "-" + $tag + "-" + $run_receipt), | |
"content_type": "text/plain" | |
}]')" | |
if [ -n "${{ steps.write_usns.outputs.usns }}" ]; then | |
assets="$(jq --compact-output \ | |
--arg tag "${{ steps.tag.outputs.tag }}" \ | |
--arg repo "${{ steps.repo_name.outputs.github_repo_name }}" \ | |
--arg usn_path "${{ steps.write_usns.outputs.usns }}" \ | |
--arg usn_name "${{ env.PATCHED_USNS_FILENAME }}" \ | |
'. += [ | |
{ | |
"path": $usn_path, | |
"name": ($repo + "-" + $tag + "-" + $usn_name), | |
"content_type": "text/plain" | |
} | |
]' <<< "${assets}")" | |
fi | |
printf "assets=%s\n" "${assets}" >> "$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: ${{ steps.notes.outputs.release_body }} | |
draft: false | |
assets: ${{ steps.assets.outputs.assets }} | |
failure: | |
name: Alert on Failure | |
runs-on: ubuntu-24.04 | |
needs: [poll_usns, create_stack, diff, test, release, packages_changed, stack_files_changed] | |
if: ${{ always() && 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}}). | |
Please take a look to ensure CVE patches can be released. (cc @paketo-buildpacks/stacks-maintainers). | |
comment_body: | | |
Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} |