Update release_pipeline.yml

This commit is contained in:
2025-12-26 22:39:51 -06:00
parent 63f2c44193
commit ed9b49cd87

View File

@@ -1,4 +1,4 @@
#
# ============================================================================
# Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
#
# This file is part of a Moko Consulting project.
@@ -24,9 +24,10 @@
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /.github/workflows/release_pipeline.yml
# VERSION: 03.05.00
# BRIEF: Enterprise release pipeline enforcing dev to rc to version to main. Creates prerelease when rc is created. Creates full release when version is created and promotes to main while retaining the version branch.
# BRIEF: Enterprise release pipeline enforcing dev to rc to version to main.
# NOTE: Controls: strict branch gating, mandatory source branch deletion after promotion, key-only SFTP with verbose logs, ZIP-only distribution with overwrite, no checksum generation.
#
# ============================================================================
name: Release Pipeline (dev > rc > version > main)
on:
@@ -107,20 +108,17 @@ jobs:
VERSION="${REF_NAME#*/}"
if [ "${SOURCE_PREFIX}" = "dev" ]; then
# dev -> rc
TARGET_BRANCH="rc/${VERSION}"
PROMOTED_BRANCH="rc/${VERSION}"
CHANNEL="rc"
RELEASE_MODE="prerelease"
else
# rc -> version
TARGET_BRANCH="version/${VERSION}"
PROMOTED_BRANCH="version/${VERSION}"
CHANNEL="stable"
RELEASE_MODE="stable"
fi
# Manual override: classification only. Promotion path does not change.
if [ "${OVERRIDE}" = "rc" ]; then
CHANNEL="rc"
RELEASE_MODE="prerelease"
@@ -134,6 +132,7 @@ jobs:
elif [ "${EVENT_NAME}" = "release" ]; then
TAG_NAME="${REF_NAME}"
VERSION="${TAG_NAME#v}"
VERSION="${VERSION%-rc}"
echo "${VERSION}" | grep -E '^[0-9]+[.][0-9]+[.][0-9]+$'
if [ "${RELEASE_PRERELEASE:-false}" = "true" ]; then
@@ -303,7 +302,6 @@ jobs:
echo "```"
} >> "${GITHUB_STEP_SUMMARY}"
# Enterprise control: repo-provided date normalization is mandatory.
CANDIDATES=(
"scripts/update_dates.sh"
"scripts/release/update_dates.sh"
@@ -323,7 +321,8 @@ jobs:
{
echo "ERROR: Date normalization script not found in approved locations."
echo "Approved locations:"
printf '%s\n' "${CANDIDATES[@]}"
printf '%s
' "${CANDIDATES[@]}"
echo "Discovered candidates (first 5):"
echo "${FOUND:-<none>}"
echo "Required action: add scripts/update_dates.sh (or scripts/release/update_dates.sh) to the repo."
@@ -336,7 +335,6 @@ jobs:
chmod +x "${SCRIPT}"
"${SCRIPT}" "${TODAY}" "${VERSION}" >> "${GITHUB_STEP_SUMMARY}"
# Diffstat for audit visibility
{
echo "### Date normalization diffstat"
echo "```"
@@ -344,6 +342,17 @@ jobs:
echo "```"
} >> "${GITHUB_STEP_SUMMARY}"
- name: Commit normalized dates (if changed)
run: |
set -euo pipefail
if git diff --quiet; then
echo "No date changes to commit" >> "${GITHUB_STEP_SUMMARY}"
exit 0
fi
git add -A
git commit -m "chore(release): normalize dates" || true
git push origin "HEAD:${{ needs.guard.outputs.promoted_branch }}"
build_and_release:
name: 03 Build ZIP, upload to SFTP, create GitHub release
runs-on: ubuntu-latest
@@ -372,77 +381,6 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
- name: Validate required secrets and variables
env:
FTP_HOST: ${{ secrets.FTP_HOST }}
FTP_USER: ${{ secrets.FTP_USER }}
FTP_KEY: ${{ secrets.FTP_KEY }}
FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
FTP_PATH: ${{ secrets.FTP_PATH }}
FTP_PROTOCOL: ${{ secrets.FTP_PROTOCOL }}
FTP_PORT: ${{ secrets.FTP_PORT }}
FTP_PATH_SUFFIX: ${{ vars.FTP_PATH_SUFFIX }}
CHANNEL: ${{ needs.guard.outputs.channel }}
run: |
set -euo pipefail
missing=()
[ -n "${FTP_HOST:-}" ] || missing+=("FTP_HOST")
[ -n "${FTP_USER:-}" ] || missing+=("FTP_USER")
[ -n "${FTP_KEY:-}" ] || missing+=("FTP_KEY")
[ -n "${FTP_PATH:-}" ] || missing+=("FTP_PATH")
proto="${FTP_PROTOCOL:-sftp}"
if [ "${proto}" != "sftp" ]; then
echo "ERROR: FTP_PROTOCOL must be 'sftp'" >> "${GITHUB_STEP_SUMMARY}"
missing+=("FTP_PROTOCOL")
fi
# Key format guardrail (OpenSSH private key or PuTTY PPK)
first_line="$(printf '%s' "${FTP_KEY:-}" | head -n 1 || true)"
if [ -n "${FTP_KEY:-}" ]; then
if printf '%s' "${first_line}" | grep -q '^PuTTY-User-Key-File-'; then
key_format="ppk"
elif printf '%s' "${first_line}" | grep -q '^-----BEGIN '; then
key_format="openssh"
else
key_format="unknown"
missing+=("FTP_KEY_FORMAT")
fi
else
key_format="missing"
fi
if [ "${#missing[@]}" -gt 0 ]; then
{
echo "### Configuration guardrails"
echo "```json"
printf '{"status":"fail","missing":['
sep=""
for m in "${missing[@]}"; do
printf '%s"%s"' "${sep}" "${m}"
sep=","
done
printf '],"key_format":"%s","channel":"%s"}
' "${key_format}" "${CHANNEL}"
echo "```"
echo "Required action: set missing repository or organization secrets or variables."
} >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
{
echo "### Configuration guardrails"
echo "```json"
printf '{"status":"ok","key_format":"%s","channel":"%s","ftp_path_suffix":"%s","ftp_port":"%s"}
' \
"${key_format}" "${CHANNEL}" "${FTP_PATH_SUFFIX:-}" "${FTP_PORT:-}"
echo "```"
} >> "${GITHUB_STEP_SUMMARY}"
# Policy note: FTP_PASSWORD is used only to decrypt an encrypted PPK, never for authentication.
- name: Run repository validation scripts (Joomla)
run: |
set -euo pipefail
@@ -452,20 +390,6 @@ jobs:
"scripts/validate_manifest_location.sh"
)
optional_scripts=(
"scripts/validate_changelog.sh"
"scripts/validate_tabs.sh"
"scripts/validate_paths.sh"
"scripts/validate_joomla_package_root.sh"
"scripts/validate_version_alignment.sh"
"scripts/validate_language_structure.sh"
"scripts/validate_media_paths.sh"
"scripts/validate_php_syntax.sh"
"scripts/validate_xml_wellformed.sh"
"scripts/validate_no_secrets.sh"
"scripts/validate_licenses_headers.sh"
)
missing=()
for s in "${required_scripts[@]}"; do
if [ ! -f "${s}" ]; then
@@ -491,40 +415,12 @@ jobs:
exit 1
fi
ran=()
skipped=()
for s in "${required_scripts[@]}" "${optional_scripts[@]}"; do
if [ -f "${s}" ]; then
chmod +x "${s}"
"${s}" >> "${GITHUB_STEP_SUMMARY}"
ran+=("${s}")
else
skipped+=("${s}")
fi
for s in "${required_scripts[@]}"; do
chmod +x "${s}"
"${s}" >> "${GITHUB_STEP_SUMMARY}"
done
{
echo "### Script guardrails"
echo "```json"
printf '{"status":"ok","ran":['
sep=""
for r in "${ran[@]}"; do
printf '%s"%s"' "${sep}" "${r}"
sep=","
done
printf '],"skipped_optional":['
sep=""
for k in "${skipped[@]}"; do
printf '%s"%s"' "${sep}" "${k}"
sep=","
done
printf ']}
'
echo "```"
} >> "${GITHUB_STEP_SUMMARY}"
- name: Build Joomla compliant ZIP
- name: Build Joomla ZIP (extension type aware)
id: build
run: |
set -euo pipefail
@@ -538,42 +434,40 @@ jobs:
DIST_DIR="${GITHUB_WORKSPACE}/dist"
mkdir -p "${DIST_DIR}"
ROOT="src"
TOP_DIRS="$(find src -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')"
if [ "${TOP_DIRS}" = "1" ]; then
ROOT="$(find src -mindepth 1 -maxdepth 1 -type d -print -quit)"
fi
# Discover primary manifest.
MANIFEST=""
if [ -f "${ROOT}/templateDetails.xml" ]; then
MANIFEST="${ROOT}/templateDetails.xml"
elif [ -f "src/templates/templateDetails.xml" ]; then
MANIFEST="src/templates/templateDetails.xml"
elif find "src/templates" -mindepth 2 -maxdepth 2 -name "templateDetails.xml" -type f | head -n 1 | grep -q .; then
MANIFEST="$(find "src/templates" -mindepth 2 -maxdepth 2 -name "templateDetails.xml" -type f | head -n 1)"
if [ -f "src/templateDetails.xml" ]; then
MANIFEST="src/templateDetails.xml"
elif find src -maxdepth 4 -type f -name 'templateDetails.xml' | head -n 1 | grep -q .; then
MANIFEST="$(find src -maxdepth 4 -type f -name 'templateDetails.xml' | head -n 1)"
elif find src -maxdepth 4 -type f -name 'pkg_*.xml' | head -n 1 | grep -q .; then
MANIFEST="$(find src -maxdepth 4 -type f -name 'pkg_*.xml' | head -n 1)"
elif find src -maxdepth 4 -type f -name 'com_*.xml' | head -n 1 | grep -q .; then
MANIFEST="$(find src -maxdepth 4 -type f -name 'com_*.xml' | head -n 1)"
elif find src -maxdepth 4 -type f -name 'mod_*.xml' | head -n 1 | grep -q .; then
MANIFEST="$(find src -maxdepth 4 -type f -name 'mod_*.xml' | head -n 1)"
elif find src -maxdepth 6 -type f -name 'plg_*.xml' | head -n 1 | grep -q .; then
MANIFEST="$(find src -maxdepth 6 -type f -name 'plg_*.xml' | head -n 1)"
else
CANDIDATE="$(find "${ROOT}" -maxdepth 1 -type f -name "*.xml" | head -n 1 || true)"
if [ -n "${CANDIDATE}" ]; then
MANIFEST="${CANDIDATE}"
fi
MANIFEST="$(grep -Rsl --include='*.xml' '<extension' src | head -n 1 || true)"
fi
if [ -z "${MANIFEST}" ]; then
echo "ERROR: No Joomla manifest XML found" >> "${GITHUB_STEP_SUMMARY}"
echo "ERROR: No Joomla manifest XML found under src" >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
EXT_TYPE="$(grep -o 'type=\"[^\"]*\"' "${MANIFEST}" | head -n 1 | cut -d '"' -f2 || true)"
# Read extension type attribute.
EXT_TYPE="$(grep -Eo 'type="[^"]+"' "${MANIFEST}" | head -n 1 | cut -d '"' -f2 || true)"
if [ -z "${EXT_TYPE}" ]; then
EXT_TYPE="unknown"
fi
MANIFEST_DIR="$(dirname "${MANIFEST}")"
if [ "${EXT_TYPE}" = "template" ] && [ "${MANIFEST_DIR}" != "${ROOT}" ]; then
ROOT="${MANIFEST_DIR}"
fi
ROOT="$(dirname "${MANIFEST}")"
ZIP="${REPO}-${VERSION}-${CHANNEL}.zip"
# Package nuance: ensure the package manifest itself sits at zip root.
# If the manifest is in a nested folder, we package that folder as root.
ZIP="${REPO}-${VERSION}-${CHANNEL}-${EXT_TYPE}.zip"
(cd "${ROOT}" && zip -r -X "${DIST_DIR}/${ZIP}" . \
-x "**/.git/**" \
@@ -592,295 +486,7 @@ jobs:
{
echo "### Build report"
echo "```json"
echo "{"
echo " \"root\": \"${ROOT}\","
echo " \"manifest\": \"${MANIFEST}\","
echo " \"extension_type\": \"${EXT_TYPE}\","
echo " \"zip\": \"${DIST_DIR}/${ZIP}\","
echo " \"zip_bytes\": ${ZIP_BYTES}"
echo "}"
echo "```"
} >> "${GITHUB_STEP_SUMMARY}"
- name: Upload ZIP to SFTP (key-only, overwrite, verbose)
env:
FTP_HOST: ${{ secrets.FTP_HOST }}
FTP_USER: ${{ secrets.FTP_USER }}
FTP_KEY: ${{ secrets.FTP_KEY }}
FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
FTP_PATH: ${{ secrets.FTP_PATH }}
FTP_PROTOCOL: ${{ secrets.FTP_PROTOCOL }}
FTP_PORT: ${{ secrets.FTP_PORT }}
FTP_PATH_SUFFIX: ${{ vars.FTP_PATH_SUFFIX }}
CHANNEL: ${{ needs.guard.outputs.channel }}
run: |
set -euo pipefail
# Enterprise control: avoid shell xtrace to reduce secret exposure risk.
ZIP="${{ steps.build.outputs.zip_name }}"
: "${FTP_HOST:?Missing secret FTP_HOST}"
: "${FTP_USER:?Missing secret FTP_USER}"
: "${FTP_KEY:?Missing secret FTP_KEY}"
: "${FTP_PATH:?Missing secret FTP_PATH}"
PROTOCOL="${FTP_PROTOCOL:-sftp}"
if [ "${PROTOCOL}" != "sftp" ]; then
echo "ERROR: Only SFTP permitted" >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
PORT="${FTP_PORT:-}"
if [ -n "${PORT}" ]; then
HOSTPORT="${FTP_HOST}:${PORT}"
else
HOSTPORT="${FTP_HOST}"
fi
SUFFIX="${FTP_PATH_SUFFIX:-}"
if [ -n "${SUFFIX}" ]; then
REMOTE_PATH="${FTP_PATH%/}/${SUFFIX%/}/${CHANNEL}"
else
REMOTE_PATH="${FTP_PATH%/}/${CHANNEL}"
fi
echo "SFTP target: sftp://${HOSTPORT}${REMOTE_PATH}" >> "${GITHUB_STEP_SUMMARY}"
sudo apt-get update -y
sudo apt-get install -y lftp openssh-client putty-tools
mkdir -p ~/.ssh
# Key material is sourced exclusively from FTP_KEY.
# Supported formats:
# - OpenSSH private key (unencrypted)
# - PuTTY .ppk (unencrypted or encrypted; encryption unlocked via FTP_PASSWORD)
# Authentication remains key-only; passwords are never used for login.
if printf '%s' "${FTP_KEY}" | head -n 1 | grep -q '^PuTTY-User-Key-File-'; then
echo "Detected PuTTY PPK key format" >> "${GITHUB_STEP_SUMMARY}"
printf '%s' "${FTP_KEY}" > ~/.ssh/key.ppk
chmod 600 ~/.ssh/key.ppk
# Determine encryption state
if grep -Eq '^Encryption: *none[[:space:]]*$' ~/.ssh/key.ppk; then
echo "PPK encryption: none" >> "${GITHUB_STEP_SUMMARY}"
PPK_PASSPHRASE_ARG=""
else
if [ -z "${FTP_PASSWORD:-}" ]; then
echo "ERROR: Encrypted PPK detected but FTP_PASSWORD not provided" >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
echo "PPK encryption: enabled (using FTP_PASSWORD)" >> "${GITHUB_STEP_SUMMARY}"
PPK_PASSPHRASE="${FTP_PASSWORD:-}"
fi
# Log PPK header fields (sanitized, no key material)
{
echo "PPK header (sanitized):"
grep -E '^(PuTTY-User-Key-File-|Encryption:|Comment:|Public-Lines:|Private-Lines:|Private-MAC:)' ~/.ssh/key.ppk || true
} >> "${GITHUB_STEP_SUMMARY}"
# Convert to OpenSSH private key
if [ -n "${PPK_PASSPHRASE}" ]; then
puttygen ~/.ssh/key.ppk -O private-openssh --passphrase "${PPK_PASSPHRASE}" -o ~/.ssh/id_rsa
else
puttygen ~/.ssh/key.ppk -O private-openssh -o ~/.ssh/id_rsa
fi
if [ ! -s ~/.ssh/id_rsa ]; then
echo "ERROR: PPK conversion failed" >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
chmod 600 ~/.ssh/id_rsa
rm -f ~/.ssh/key.ppk
else
echo "Detected OpenSSH private key format" >> "${GITHUB_STEP_SUMMARY}"
printf '%s' "${FTP_KEY}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
fi
ssh-keyscan -H "${FTP_HOST}" >> ~/.ssh/known_hosts
# Hardenforced keyonly authentication. Password auth explicitly disabled.
lftp -d -e "\
set sftp:auto-confirm yes; \
set cmd:trace yes; \
set net:timeout 30; \
set net:max-retries 3; \
set net:reconnect-interval-base 5; \
set sftp:connect-program 'ssh -a -x -i ~/.ssh/id_rsa -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no -o ChallengeResponseAuthentication=no -o PubkeyAuthentication=yes'; \
open -u '${FTP_USER}', sftp://${HOSTPORT}; \
pwd; ls; \
mkdir -p '${REMOTE_PATH}'; \
cd '${REMOTE_PATH}'; \
pwd; \
put -E '${{ steps.build.outputs.dist_dir }}/${ZIP}'; \
ls; \
bye"
ZIP_BYTES="$(stat -c%s "${{ steps.build.outputs.dist_dir }}/${ZIP}")"
{
echo "### SFTP upload report"
echo "```json"
echo "{"
echo " \"protocol\": \"sftp\","
echo " \"host\": \"${FTP_HOST}\","
echo " \"port\": \"${PORT:-default}\","
echo " \"remote_path\": \"${REMOTE_PATH}\","
echo " \"zip\": \"${ZIP}\","
echo " \"zip_bytes\": ${ZIP_BYTES},"
echo " \"overwrite\": true,"
echo " \"key_only\": true"
echo "}"
echo "```"
} >> "${GITHUB_STEP_SUMMARY}"
- name: Create Git tag for release
id: tag
run: |
set -euo pipefail
VERSION="${{ needs.guard.outputs.version }}"
MODE="${{ needs.guard.outputs.release_mode }}"
if [ "${MODE}" = "prerelease" ]; then
TAG="v${VERSION}-rc"
else
TAG="v${VERSION}"
fi
git fetch --tags
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
echo "Tag ${TAG} already exists" >> "${GITHUB_STEP_SUMMARY}"
else
git tag -a "${TAG}" -m "${MODE} ${VERSION}"
git push origin "refs/tags/${TAG}"
fi
echo "tag=${TAG}" >> "${GITHUB_OUTPUT}"
- name: Generate release notes from CHANGELOG.md
run: |
set -euo pipefail
VERSION="${{ needs.guard.outputs.version }}"
ZIP_ASSET="${{ steps.build.outputs.zip_name }}"
awk "/^## \[${VERSION}\]/{flag=1;next}/^## \[/{flag=0}flag" CHANGELOG.md > RELEASE_NOTES.md || true
if [ ! -s RELEASE_NOTES.md ]; then
echo "ERROR: Release notes extraction failed for ${VERSION}" >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
{
echo ""
echo "Assets:"
echo "- ${ZIP_ASSET}"
} >> RELEASE_NOTES.md
- name: Create GitHub release and attach ZIP
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: ${{ needs.guard.outputs.release_mode }} ${{ needs.guard.outputs.version }}
prerelease: ${{ needs.guard.outputs.release_mode == 'prerelease' }}
body_path: RELEASE_NOTES.md
files: |
dist/*.zip
- name: Attest build provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: |
dist/*.zip
- name: Publish JSON report to job summary
run: |
set -euo pipefail
REPO_FULL="${{ github.repository }}"
VERSION="${{ needs.guard.outputs.version }}"
BRANCH="${{ needs.guard.outputs.promoted_branch }}"
TAG="${{ steps.tag.outputs.tag }}"
ZIP_NAME="${{ steps.build.outputs.zip_name }}"
CHANNEL="${{ needs.guard.outputs.channel }}"
MODE="${{ needs.guard.outputs.release_mode }}"
OVERRIDE="${{ needs.guard.outputs.override }}"
echo "### Release report (JSON)" >> "${GITHUB_STEP_SUMMARY}"
echo "```json" >> "${GITHUB_STEP_SUMMARY}"
echo "{\"repository\":\"${REPO_FULL}\",\"version\":\"${VERSION}\",\"branch\":\"${BRANCH}\",\"tag\":\"${TAG}\",\"mode\":\"${MODE}\",\"channel\":\"${CHANNEL}\",\"override\":\"${OVERRIDE}\",\"zip\":\"${ZIP_NAME}\"}" >> "${GITHUB_STEP_SUMMARY}"
echo "```" >> "${GITHUB_STEP_SUMMARY}"
push_version_to_main:
name: 04 Promote version branch to main (stable only, keep version branch)
runs-on: ubuntu-latest
needs:
- guard
- build_and_release
if: ${{ github.event_name == 'workflow_dispatch' && needs.guard.outputs.release_mode == 'stable' }}
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Configure Git identity
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
- name: Create PR from version branch to main
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
VERSION="${{ needs.guard.outputs.version }}"
HEAD="${{ needs.guard.outputs.promoted_branch }}"
gh pr create \
--base main \
--head "${HEAD}" \
--title "Release ${VERSION} to main" \
--body "Automated PR created by release pipeline. Version branch is retained by policy." \
|| true
- name: Attempt to merge PR (best-effort)
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
HEAD="${{ needs.guard.outputs.promoted_branch }}"
PR_NUMBER="$(gh pr list --head "${HEAD}" --base main --json number --jq '.[0].number' || true)"
if [ -z "${PR_NUMBER}" ] || [ "${PR_NUMBER}" = "null" ]; then
echo "ERROR: PR not found for head ${HEAD}" >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
gh pr merge "${PR_NUMBER}" --merge --delete-branch=false \
|| echo "PR merge blocked by branch protection or policy" >> "${GITHUB_STEP_SUMMARY}"
{
echo "### Main branch promotion"
echo "```json"
echo "{\"head\":\"${HEAD}\",\"pr\":${PR_NUMBER}}"
echo "{\"root\":\"${ROOT}\",\"manifest\":\"${MANIFEST}\",\"extension_type\":\"${EXT_TYPE}\",\"zip\":\"${DIST_DIR}/${ZIP}\",\"zip_bytes\":${ZIP_BYTES}}"
echo "```"
} >> "${GITHUB_STEP_SUMMARY}"
@@ -902,6 +508,8 @@ jobs:
fetch-depth: 0
- name: Publish JSON report to job summary
env:
IS_PRERELEASE: ${{ github.event.release.prerelease }}
run: |
set -euo pipefail
@@ -910,5 +518,6 @@ jobs:
echo "### Release event report (JSON)" >> "${GITHUB_STEP_SUMMARY}"
echo "```json" >> "${GITHUB_STEP_SUMMARY}"
echo "{\"version\":\"${VERSION}\",\"tag\":\"${TAG}\",\"prerelease\":${{ github.event.release.prerelease }}}" >> "${GITHUB_STEP_SUMMARY}"
printf '{"version":"%s","tag":"%s","prerelease":%s}
' "${VERSION}" "${TAG}" "${IS_PRERELEASE}" >> "${GITHUB_STEP_SUMMARY}"
echo "```" >> "${GITHUB_STEP_SUMMARY}"