Update release_pipeline.yml
This commit is contained in:
631
.github/workflows/release_pipeline.yml
vendored
631
.github/workflows/release_pipeline.yml
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
# Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
#
|
#
|
||||||
@@ -16,536 +18,103 @@
|
|||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program (./LICENSE.md).
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: GitHub.Workflow
|
|
||||||
# INGROUP: MokoStandards.Release
|
|
||||||
# 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.
|
|
||||||
# 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)
|
# ============================================================================
|
||||||
|
# FILE INFORMATION
|
||||||
on:
|
# ============================================================================
|
||||||
workflow_dispatch:
|
# DEFGROUP: Script.Library
|
||||||
inputs:
|
# INGROUP: RepoHealth
|
||||||
release_classification:
|
# REPO: https://github.com/mokoconsulting-tech
|
||||||
description: "Manual override for classification. auto follows branch policy; rc forces prerelease behavior; stable forces full release behavior."
|
# PATH: /scripts/lib/find_files.sh
|
||||||
required: true
|
# VERSION: 01.00.00
|
||||||
default: auto
|
# BRIEF: Find files by glob patterns with standard ignore rules for CI checks
|
||||||
type: choice
|
# NOTE:
|
||||||
options:
|
# ============================================================================
|
||||||
- auto
|
|
||||||
- rc
|
set -eu
|
||||||
- stable
|
|
||||||
release:
|
# Shared utilities
|
||||||
types:
|
. "$(dirname "$0")/common.sh"
|
||||||
- created
|
|
||||||
- prereleased
|
# ----------------------------------------------------------------------------
|
||||||
- published
|
# Purpose:
|
||||||
|
# - Provide a consistent, reusable file discovery primitive for repo scripts.
|
||||||
concurrency:
|
# - Support multiple glob patterns.
|
||||||
group: release-pipeline-${{ github.ref_name }}
|
# - Apply standard ignore rules to reduce noise (vendor, node_modules, .git).
|
||||||
cancel-in-progress: false
|
# - Output one path per line, relative to repo root.
|
||||||
|
#
|
||||||
defaults:
|
# Usage:
|
||||||
run:
|
# ./scripts/lib/find_files.sh <glob> [<glob> ...]
|
||||||
shell: bash
|
#
|
||||||
|
# Examples:
|
||||||
permissions:
|
# ./scripts/lib/find_files.sh "*.yml" "*.yaml"
|
||||||
contents: read
|
# ./scripts/lib/find_files.sh "src/**/*.php" "tests/**/*.php"
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
jobs:
|
|
||||||
guard:
|
ROOT="$(script_root)"
|
||||||
name: 00 Guard and derive promotion metadata
|
|
||||||
runs-on: ubuntu-latest
|
if [ "${1:-}" = "" ]; then
|
||||||
|
die "Usage: $0 <glob> [<glob> ...]"
|
||||||
outputs:
|
fi
|
||||||
version: ${{ steps.meta.outputs.version }}
|
|
||||||
source_branch: ${{ steps.meta.outputs.source_branch }}
|
require_cmd find
|
||||||
source_prefix: ${{ steps.meta.outputs.source_prefix }}
|
require_cmd sed
|
||||||
target_branch: ${{ steps.meta.outputs.target_branch }}
|
|
||||||
promoted_branch: ${{ steps.meta.outputs.promoted_branch }}
|
# Standard excludes (pragmatic defaults for CI)
|
||||||
today_utc: ${{ steps.meta.outputs.today_utc }}
|
# Note: Keep these broad to avoid scanning generated or third-party content.
|
||||||
channel: ${{ steps.meta.outputs.channel }}
|
EXCLUDES='
|
||||||
release_mode: ${{ steps.meta.outputs.release_mode }}
|
-path "*/.git/*" -o
|
||||||
override: ${{ steps.meta.outputs.override }}
|
-path "*/.github/*/node_modules/*" -o
|
||||||
|
-path "*/node_modules/*" -o
|
||||||
steps:
|
-path "*/vendor/*" -o
|
||||||
- name: Validate trigger and extract metadata
|
-path "*/dist/*" -o
|
||||||
id: meta
|
-path "*/build/*" -o
|
||||||
env:
|
-path "*/cache/*" -o
|
||||||
RELEASE_CLASSIFICATION: ${{ github.event.inputs.release_classification }}
|
-path "*/tmp/*" -o
|
||||||
RELEASE_PRERELEASE: ${{ github.event.release.prerelease }}
|
-path "*/.tmp/*" -o
|
||||||
run: |
|
-path "*/.cache/*"
|
||||||
set -euxo pipefail
|
'
|
||||||
|
|
||||||
EVENT_NAME="${GITHUB_EVENT_NAME}"
|
# Convert a glob (bash-like) to a find -path pattern.
|
||||||
REF_NAME="${GITHUB_REF_NAME}"
|
# - Supports ** for "any directories" by translating to *
|
||||||
|
# - Ensures leading */ so patterns apply anywhere under repo root
|
||||||
VERSION=""
|
glob_to_find_path() {
|
||||||
SOURCE_BRANCH=""
|
g="$1"
|
||||||
SOURCE_PREFIX=""
|
|
||||||
TARGET_BRANCH=""
|
# normalize path separators for WSL/CI compatibility
|
||||||
PROMOTED_BRANCH=""
|
g="$(normalize_path "$g")"
|
||||||
CHANNEL=""
|
|
||||||
RELEASE_MODE="none"
|
# translate ** to * (find -path uses shell glob semantics)
|
||||||
|
g="$(printf '%s' "$g" | sed 's|\*\*|*|g')"
|
||||||
OVERRIDE="${RELEASE_CLASSIFICATION:-auto}"
|
|
||||||
if [ -z "${OVERRIDE}" ]; then
|
case "$g" in
|
||||||
OVERRIDE="auto"
|
/*) printf '%s\n' "$g" ;;
|
||||||
fi
|
*) printf '%s\n' "*/$g" ;;
|
||||||
|
esac
|
||||||
if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then
|
}
|
||||||
echo "${REF_NAME}" | grep -E '^(dev|rc)/[0-9]+[.][0-9]+[.][0-9]+$'
|
|
||||||
|
# Build a single find invocation that ORs all patterns.
|
||||||
SOURCE_BRANCH="${REF_NAME}"
|
# Shell portability note: avoid arrays; build an expression string.
|
||||||
SOURCE_PREFIX="${REF_NAME%%/*}"
|
PAT_EXPR=""
|
||||||
VERSION="${REF_NAME#*/}"
|
for GLOB in "$@"; do
|
||||||
|
P="$(glob_to_find_path "$GLOB")"
|
||||||
if [ "${SOURCE_PREFIX}" = "dev" ]; then
|
if [ -z "$PAT_EXPR" ]; then
|
||||||
TARGET_BRANCH="rc/${VERSION}"
|
PAT_EXPR="-path \"$P\""
|
||||||
PROMOTED_BRANCH="rc/${VERSION}"
|
else
|
||||||
CHANNEL="rc"
|
PAT_EXPR="$PAT_EXPR -o -path \"$P\""
|
||||||
RELEASE_MODE="prerelease"
|
fi
|
||||||
else
|
done
|
||||||
TARGET_BRANCH="version/${VERSION}"
|
|
||||||
PROMOTED_BRANCH="version/${VERSION}"
|
# Execute find and emit relative paths.
|
||||||
CHANNEL="stable"
|
# - Use eval to apply the constructed predicate string safely as a single expression.
|
||||||
RELEASE_MODE="stable"
|
# - We scope to files only.
|
||||||
fi
|
# - We prune excluded directories.
|
||||||
|
cd "$ROOT"
|
||||||
if [ "${OVERRIDE}" = "rc" ]; then
|
|
||||||
CHANNEL="rc"
|
# shellcheck disable=SC2086
|
||||||
RELEASE_MODE="prerelease"
|
eval "find . \\( $EXCLUDES \\) -prune -o -type f \\( $PAT_EXPR \\) -print" \
|
||||||
elif [ "${OVERRIDE}" = "stable" ]; then
|
| sed 's|^\./||' \
|
||||||
CHANNEL="stable"
|
| sed '/^$/d' \
|
||||||
RELEASE_MODE="stable"
|
| sort -u
|
||||||
else
|
|
||||||
OVERRIDE="auto"
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
CHANNEL="rc"
|
|
||||||
RELEASE_MODE="prerelease"
|
|
||||||
else
|
|
||||||
CHANNEL="stable"
|
|
||||||
RELEASE_MODE="stable"
|
|
||||||
fi
|
|
||||||
|
|
||||||
OVERRIDE="auto"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "ERROR: Unsupported trigger ${EVENT_NAME}" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
TODAY_UTC="$(date -u +%Y-%m-%d)"
|
|
||||||
|
|
||||||
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "source_branch=${SOURCE_BRANCH}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "source_prefix=${SOURCE_PREFIX}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "target_branch=${TARGET_BRANCH}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "promoted_branch=${PROMOTED_BRANCH}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "today_utc=${TODAY_UTC}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "channel=${CHANNEL}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "release_mode=${RELEASE_MODE}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "override=${OVERRIDE}" >> "${GITHUB_OUTPUT}"
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "### Guard report"
|
|
||||||
echo "```json"
|
|
||||||
echo "{"
|
|
||||||
echo " \"repository\": \"${GITHUB_REPOSITORY}\","
|
|
||||||
echo " \"workflow\": \"${GITHUB_WORKFLOW}\","
|
|
||||||
echo " \"job\": \"${GITHUB_JOB}\","
|
|
||||||
echo " \"run_id\": ${GITHUB_RUN_ID},"
|
|
||||||
echo " \"run_number\": ${GITHUB_RUN_NUMBER},"
|
|
||||||
echo " \"run_attempt\": ${GITHUB_RUN_ATTEMPT},"
|
|
||||||
echo " \"run_url\": \"${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}\","
|
|
||||||
echo " \"actor\": \"${GITHUB_ACTOR}\","
|
|
||||||
echo " \"sha\": \"${GITHUB_SHA}\","
|
|
||||||
echo " \"event\": \"${EVENT_NAME}\","
|
|
||||||
echo " \"ref\": \"${REF_NAME}\","
|
|
||||||
echo " \"version\": \"${VERSION}\","
|
|
||||||
echo " \"source_branch\": \"${SOURCE_BRANCH}\","
|
|
||||||
echo " \"target_branch\": \"${TARGET_BRANCH}\","
|
|
||||||
echo " \"promoted_branch\": \"${PROMOTED_BRANCH}\","
|
|
||||||
echo " \"channel\": \"${CHANNEL}\","
|
|
||||||
echo " \"release_mode\": \"${RELEASE_MODE}\","
|
|
||||||
echo " \"override\": \"${OVERRIDE}\","
|
|
||||||
echo " \"today_utc\": \"${TODAY_UTC}\""
|
|
||||||
echo "}"
|
|
||||||
echo "```"
|
|
||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|
||||||
promote_branch:
|
|
||||||
name: 01 Promote branch and delete source
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: guard
|
|
||||||
|
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout source branch
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ needs.guard.outputs.source_branch }}
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Configure Git identity
|
|
||||||
run: |
|
|
||||||
set -euxo 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: Enforce promotion preconditions
|
|
||||||
run: |
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
SRC="${{ needs.guard.outputs.source_branch }}"
|
|
||||||
DST="${{ needs.guard.outputs.target_branch }}"
|
|
||||||
|
|
||||||
git fetch origin --prune
|
|
||||||
|
|
||||||
if [ -z "${SRC}" ] || [ -z "${DST}" ]; then
|
|
||||||
echo "ERROR: guard did not emit SRC or DST" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! git show-ref --verify --quiet "refs/remotes/origin/${SRC}"; then
|
|
||||||
echo "ERROR: origin/${SRC} not found" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if git show-ref --verify --quiet "refs/remotes/origin/${DST}"; then
|
|
||||||
echo "ERROR: origin/${DST} already exists" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Promote and delete source
|
|
||||||
run: |
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
SRC="${{ needs.guard.outputs.source_branch }}"
|
|
||||||
DST="${{ needs.guard.outputs.target_branch }}"
|
|
||||||
|
|
||||||
git checkout -B "${DST}" "origin/${SRC}"
|
|
||||||
git push origin "${DST}"
|
|
||||||
git push origin --delete "${SRC}"
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "### Promotion report"
|
|
||||||
echo "```json"
|
|
||||||
echo "{"
|
|
||||||
echo " \"repository\": \"${GITHUB_REPOSITORY}\","
|
|
||||||
echo " \"workflow\": \"${GITHUB_WORKFLOW}\","
|
|
||||||
echo " \"job\": \"${GITHUB_JOB}\","
|
|
||||||
echo " \"run_id\": ${GITHUB_RUN_ID},"
|
|
||||||
echo " \"run_number\": ${GITHUB_RUN_NUMBER},"
|
|
||||||
echo " \"run_attempt\": ${GITHUB_RUN_ATTEMPT},"
|
|
||||||
echo " \"run_url\": \"${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}\","
|
|
||||||
echo " \"actor\": \"${GITHUB_ACTOR}\","
|
|
||||||
echo " \"sha\": \"${GITHUB_SHA}\","
|
|
||||||
echo " \"promoted\": \"${SRC} -> ${DST}\","
|
|
||||||
echo " \"deleted\": \"${SRC}\""
|
|
||||||
echo "}"
|
|
||||||
echo "```"
|
|
||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|
||||||
normalize_dates:
|
|
||||||
name: 02 Normalize dates on promoted branch
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- guard
|
|
||||||
- promote_branch
|
|
||||||
|
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout promoted branch
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ needs.guard.outputs.promoted_branch }}
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Configure Git identity
|
|
||||||
run: |
|
|
||||||
set -euxo 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: Validate repo prerequisites
|
|
||||||
run: |
|
|
||||||
set -euxo pipefail
|
|
||||||
test -d src || (echo "ERROR: src directory missing" && exit 1)
|
|
||||||
test -f CHANGELOG.md || (echo "ERROR: CHANGELOG.md missing" && exit 1)
|
|
||||||
|
|
||||||
VERSION="${{ needs.guard.outputs.version }}"
|
|
||||||
|
|
||||||
if ! grep -F "## [${VERSION}] " CHANGELOG.md >/dev/null; then
|
|
||||||
echo "ERROR: CHANGELOG.md missing heading for version [${VERSION}]" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Normalize dates using repository script only
|
|
||||||
run: |
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
TODAY="${{ needs.guard.outputs.today_utc }}"
|
|
||||||
VERSION="${{ needs.guard.outputs.version }}"
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "### Date normalization (repo script only)"
|
|
||||||
echo "```json"
|
|
||||||
echo "{\"today_utc\":\"${TODAY}\",\"version\":\"${VERSION}\"}"
|
|
||||||
echo "```"
|
|
||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|
||||||
CANDIDATES=(
|
|
||||||
"scripts/update_dates.sh"
|
|
||||||
"scripts/release/update_dates.sh"
|
|
||||||
"scripts/release/update_dates"
|
|
||||||
)
|
|
||||||
|
|
||||||
SCRIPT=""
|
|
||||||
for c in "${CANDIDATES[@]}"; do
|
|
||||||
if [ -f "${c}" ]; then
|
|
||||||
SCRIPT="${c}"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "${SCRIPT}" ]; then
|
|
||||||
FOUND="$(find . -maxdepth 3 -type f \( -name 'update_dates.sh' -o -name 'update-dates.sh' \) 2>/dev/null | head -n 5 || true)"
|
|
||||||
{
|
|
||||||
echo "ERROR: Date normalization script not found in approved locations."
|
|
||||||
echo "Approved locations:"
|
|
||||||
printf '%s\n' "${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."
|
|
||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Using date script: ${SCRIPT}" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|
||||||
chmod +x "${SCRIPT}"
|
|
||||||
"${SCRIPT}" "${TODAY}" "${VERSION}" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "### Date normalization diffstat"
|
|
||||||
echo "```"
|
|
||||||
git diff --stat || true
|
|
||||||
echo "```"
|
|
||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|
||||||
- name: Commit normalized dates (if changed)
|
|
||||||
run: |
|
|
||||||
set -euxo 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
|
|
||||||
needs:
|
|
||||||
- guard
|
|
||||||
- normalize_dates
|
|
||||||
|
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
id-token: write
|
|
||||||
attestations: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout promoted branch
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ needs.guard.outputs.promoted_branch }}
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Configure Git identity
|
|
||||||
run: |
|
|
||||||
set -euxo 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: Run repository validation scripts (Joomla)
|
|
||||||
run: |
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
required_scripts=(
|
|
||||||
"scripts/validate_manifest.sh"
|
|
||||||
"scripts/validate_manifest_location.sh"
|
|
||||||
)
|
|
||||||
|
|
||||||
missing=()
|
|
||||||
for s in "${required_scripts[@]}"; do
|
|
||||||
if [ ! -f "${s}" ]; then
|
|
||||||
missing+=("${s}")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "${#missing[@]}" -gt 0 ]; then
|
|
||||||
{
|
|
||||||
echo "### Script guardrails"
|
|
||||||
echo "```json"
|
|
||||||
printf '{"status":"fail","missing_required_scripts":['
|
|
||||||
sep=""
|
|
||||||
for m in "${missing[@]}"; do
|
|
||||||
printf '%s"%s"' "${sep}" "${m}"
|
|
||||||
sep=","
|
|
||||||
done
|
|
||||||
printf ']}\n'
|
|
||||||
echo "```"
|
|
||||||
echo "Required action: add missing scripts under /scripts and ensure they are executable."
|
|
||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
for s in "${required_scripts[@]}"; do
|
|
||||||
chmod +x "${s}"
|
|
||||||
"${s}" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Build Joomla ZIP (extension type aware)
|
|
||||||
id: build
|
|
||||||
run: |
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
VERSION="${{ needs.guard.outputs.version }}"
|
|
||||||
REPO="${{ github.event.repository.name }}"
|
|
||||||
CHANNEL="${{ needs.guard.outputs.channel }}"
|
|
||||||
|
|
||||||
test -d src || (echo "ERROR: src directory missing" && exit 1)
|
|
||||||
|
|
||||||
DIST_DIR="${GITHUB_WORKSPACE}/dist"
|
|
||||||
mkdir -p "${DIST_DIR}"
|
|
||||||
|
|
||||||
# Discover primary manifest.
|
|
||||||
MANIFEST=""
|
|
||||||
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
|
|
||||||
MANIFEST="$(grep -Rsl --include='*.xml' '<extension' src | head -n 1 || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "${MANIFEST}" ]; then
|
|
||||||
echo "ERROR: No Joomla manifest XML found under src" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
ROOT="$(dirname "${MANIFEST}")"
|
|
||||||
|
|
||||||
# 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/**" \
|
|
||||||
-x "**/.github/**" \
|
|
||||||
-x "**/.DS_Store" \
|
|
||||||
-x "**/__MACOSX/**")
|
|
||||||
|
|
||||||
echo "zip_name=${ZIP}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "dist_dir=${DIST_DIR}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "root=${ROOT}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "manifest=${MANIFEST}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "ext_type=${EXT_TYPE}" >> "${GITHUB_OUTPUT}"
|
|
||||||
|
|
||||||
ZIP_BYTES="$(stat -c%s "${DIST_DIR}/${ZIP}")"
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "### Build report"
|
|
||||||
echo "```json"
|
|
||||||
echo "{\"repository\":\"${GITHUB_REPOSITORY}\",\"workflow\":\"${GITHUB_WORKFLOW}\",\"job\":\"${GITHUB_JOB}\",\"run_id\":${GITHUB_RUN_ID},\"run_number\":${GITHUB_RUN_NUMBER},\"run_attempt\":${GITHUB_RUN_ATTEMPT},\"run_url\":\"${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}\",\"actor\":\"${GITHUB_ACTOR}\",\"sha\":\"${GITHUB_SHA}\",\"root\":\"${ROOT}\",\"manifest\":\"${MANIFEST}\",\"extension_type\":\"${EXT_TYPE}\",\"zip\":\"${DIST_DIR}/${ZIP}\",\"zip_bytes\":${ZIP_BYTES}}"
|
|
||||||
echo "```"
|
|
||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|
||||||
release_event_report:
|
|
||||||
name: 99 Release event report (GitHub UI created release)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: guard
|
|
||||||
|
|
||||||
if: ${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout tag
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.ref_name }}
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Publish JSON report to job summary
|
|
||||||
env:
|
|
||||||
IS_PRERELEASE: ${{ github.event.release.prerelease }}
|
|
||||||
run: |
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
VERSION="${{ needs.guard.outputs.version }}"
|
|
||||||
TAG="${{ github.ref_name }}"
|
|
||||||
|
|
||||||
echo "### Release event report (JSON)" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
echo "```json" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
printf '{"repository":"%s","workflow":"%s","job":"%s","run_id":%s,"run_number":%s,"run_attempt":%s,"run_url":"%s","actor":"%s","sha":"%s","version":"%s","tag":"%s","prerelease":%s}
|
|
||||||
' \
|
|
||||||
"${GITHUB_REPOSITORY}" \
|
|
||||||
"${GITHUB_WORKFLOW}" \
|
|
||||||
"${GITHUB_JOB}" \
|
|
||||||
"${GITHUB_RUN_ID}" \
|
|
||||||
"${GITHUB_RUN_NUMBER}" \
|
|
||||||
"${GITHUB_RUN_ATTEMPT}" \
|
|
||||||
"${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
|
|
||||||
"${GITHUB_ACTOR}" \
|
|
||||||
"${GITHUB_SHA}" \
|
|
||||||
"${VERSION}" \
|
|
||||||
"${TAG}" \
|
|
||||||
"${IS_PRERELEASE}" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
echo "```" >> "${GITHUB_STEP_SUMMARY}"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user