diff --git a/.github/workflows/build_joomla_zip.yml b/.github/workflows/build_joomla_zip.yml deleted file mode 100644 index 1a8b7c9..0000000 --- a/.github/workflows/build_joomla_zip.yml +++ /dev/null @@ -1,332 +0,0 @@ -# ============================================================================ -# Copyright (C) 2025 Moko Consulting -# -# This file is part of a Moko Consulting project. -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# FILE INFORMATION -# DEFGROUP: MokoStandards -# INGROUP: GitHub.Workflows -# REPO: https://github.com/mokoconsulting-tech/MokoDefaults -# PATH: /.github/workflows/build_joomla_zip.yml -# VERSION: 01.00.00 -# BRIEF: Build a Joomla ZIP from /src and optionally attach it to a GitHub Release. -# NOTE: Zips the contents of /src (not the folder) to avoid nested ZIP structures. -# ============================================================================ -# -# Script details -# - Builds a ZIP artifact from the repository /src folder. -# - Runs automatically when a GitHub Release is created. -# - Can be executed manually from the Actions menu (workflow_dispatch). -# - Optionally uploads the ZIP as a Release asset. -# - Provides CI artifacts when not attaching to a Release. - -name: Build Joomla ZIP from src - -"on": - release: - types: [created] - workflow_dispatch: - inputs: - attach_to_release: - description: "Attach the generated ZIP to a GitHub Release" - required: false - default: "false" - type: choice - options: - - "false" - - "true" - release_tag: - description: "Release tag to upload to. If blank, defaults to dev/ using templateDetails.xml (example dev/03.02.00)" - required: false - default: "" - zip_name: - description: "ZIP base name without extension (defaults to repository name)" - required: false - default: "" - zip_suffix: - description: "Optional suffix appended after version (example rc1, beta, build.5)" - required: false - default: "" - prerelease: - description: "Mark the uploaded asset as prerelease when attaching to a Release" - required: false - default: "false" - type: choice - options: - - "false" - - "true" - -permissions: - contents: write - -jobs: - build-zip: - name: Build ZIP - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Validate src folder exists - shell: bash - run: | - set -euo pipefail - if [ ! -d "src" ]; then - echo "ERROR: Required folder 'src' not found at repository root." >&2 - echo "Expected: ./src" >&2 - exit 1 - fi - - - name: Resolve build parameters - id: params - shell: bash - run: | - set -euo pipefail - - REPO_NAME="${GITHUB_REPOSITORY#*/}" - - ZIP_BASE_INPUT="${{ github.event.inputs.zip_name }}" - if [ -n "${ZIP_BASE_INPUT}" ]; then - ZIP_BASE="${ZIP_BASE_INPUT}" - else - ZIP_BASE="${REPO_NAME}" - fi - - VERSION="" - SUFFIX_INPUT="${{ github.event.inputs.zip_suffix }}" - PRERELEASE_INPUT="${{ github.event.inputs.prerelease }}" - ATTACH_INPUT="${{ github.event.inputs.attach_to_release }}" - - if [ "${GITHUB_EVENT_NAME}" = "release" ]; then - ATTACH="true" - TAG_NAME="${{ github.event.release.tag_name }}" - VERSION="${TAG_NAME##*/}" - PRERELEASE="${{ github.event.release.prerelease }}" - else - if [ "${ATTACH_INPUT}" = "true" ]; then - ATTACH="true" - else - ATTACH="false" - fi - - TAG_INPUT="${{ github.event.inputs.release_tag }}" - if [ -n "${TAG_INPUT}" ]; then - TAG_NAME="${TAG_INPUT}" - VERSION="${TAG_NAME##*/}" - else - if [ ! -f "src/templateDetails.xml" ]; then - echo "ERROR: src/templateDetails.xml not found and release_tag not provided." >&2 - exit 1 - fi - - VERSION=$(grep -Eo '[0-9]+(\.[0-9]+)*' src/templateDetails.xml | head -n1 | sed -E 's###g') - if [ -z "${VERSION}" ]; then - echo "ERROR: Unable to extract from src/templateDetails.xml." >&2 - exit 1 - fi - - TAG_NAME="dev/${VERSION}" - fi - - if [ "${PRERELEASE_INPUT}" = "true" ]; then - PRERELEASE="true" - else - PRERELEASE="false" - fi - fi - - if [ -n "${VERSION}" ]; then - : - else - echo "ERROR: VERSION could not be resolved." >&2 - exit 1 - fi - - if [ -n "${SUFFIX_INPUT}" ]; then - ZIP_NAME="${ZIP_BASE}-${VERSION}-${SUFFIX_INPUT}.zip" - else - ZIP_NAME="${ZIP_BASE}-${VERSION}.zip" - fi - - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "attach=${ATTACH}" >> "$GITHUB_OUTPUT" - echo "tag_name=${TAG_NAME}" >> "$GITHUB_OUTPUT" - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT" - - echo "Resolved parameters:" >&2 - echo "- ZIP_NAME: ${ZIP_NAME}" >&2 - echo "- ATTACH_TO_RELEASE: ${ATTACH}" >&2 - echo "- TAG_NAME: ${TAG_NAME}" >&2 - echo "- VERSION: ${VERSION}" >&2 - echo "- PRERELEASE: ${PRERELEASE}" >&2 - - - name: Preflight checks - shell: bash - run: | - set -euo pipefail - - if [ "${{ steps.params.outputs.attach }}" = "true" ] && [ -z "${{ steps.params.outputs.tag_name }}" ]; then - echo "ERROR: tag_name is required when attach_to_release=true on workflow_dispatch." >&2 - echo "Provide 'release_tag' input, or run on a Release created event." >&2 - exit 1 - fi - - echo "src directory listing:" >&2 - ls -la src >&2 - - - name: Validate Joomla manifest prior to build - shell: bash - run: | - set -euo pipefail - - MANIFEST_FILE="" - - # Detect common Joomla manifest locations - if [ -f "src/templateDetails.xml" ]; then - MANIFEST_FILE="src/templateDetails.xml" - else - MANIFEST_FILE=$(find src -maxdepth 2 -name "*.xml" -print | head -n 1 || true) - fi - - if [ -z "${MANIFEST_FILE}" ]; then - echo "ERROR: No Joomla manifest XML file found in src." >&2 - exit 1 - fi - - echo "Validating manifest: ${MANIFEST_FILE}" >&2 - - # Basic XML well-formedness check - if ! xmllint --noout "${MANIFEST_FILE}"; then - echo "ERROR: Manifest XML is not well-formed." >&2 - exit 1 - fi - - # Required Joomla fields - for TAG in name version type; do - if ! grep -q "<${TAG}>" "${MANIFEST_FILE}"; then - echo "ERROR: Required <${TAG}> tag missing in manifest." >&2 - exit 1 - fi - done - - echo "Manifest validation passed." >&2 - - - name: Build ZIP from src contents - shell: bash - run: | - set -euo pipefail - - ZIP_NAME="${{ steps.params.outputs.zip_name }}" - - rm -f "${ZIP_NAME}" - - # Packaging rules - # - Always keep Joomla top level folders such as media/, language/, administrator/, etc. - # - If src contains a template folder (templateDetails.xml present in a subfolder), - # collapse ONLY that template folder contents into the ZIP root. - # - All other top level folders remain unchanged. - - TMP_DIR=$(mktemp -d) - - # Copy all top level items except template folders first - shopt -s dotglob - for ITEM in src/*; do - if [ -d "${ITEM}" ] && [ -f "${ITEM}/templateDetails.xml" ]; then - TEMPLATE_DIR="${ITEM}" - else - cp -R "${ITEM}" "${TMP_DIR}/" - fi - done - - # Collapse template folder if detected - if [ -n "${TEMPLATE_DIR:-}" ]; then - echo "Collapsing Joomla template folder into ZIP root: ${TEMPLATE_DIR}" >&2 - cp -R "${TEMPLATE_DIR}/"* "${TMP_DIR}/" - fi - - ( cd "${TMP_DIR}" && zip -r -q "${GITHUB_WORKSPACE}/${ZIP_NAME}" . ) - - rm -rf "${TMP_DIR}" - - if [ ! -f "${ZIP_NAME}" ]; then - echo "ERROR: ZIP build failed. Output file not found: ${ZIP_NAME}" >&2 - exit 1 - fi - - echo "Built ZIP:" >&2 - ls -la "${ZIP_NAME}" >&2 - - - name: Upload build artifact to workflow run - if: steps.params.outputs.attach != 'true' - uses: actions/upload-artifact@v4 - with: - name: joomla-zip - path: ${{ steps.params.outputs.zip_name }} - if-no-files-found: error - - - name: Generate Release Notes from CHANGELOG - if: steps.params.outputs.attach == 'true' - shell: bash - run: | - set -euo pipefail - - VERSION="${{ steps.params.outputs.version }}" - NOTES_FILE="release-notes-${VERSION}.md" - - if [ -f "CHANGELOG.md" ]; then - # Extract ONLY the section for the resolved version from CHANGELOG.md - # Required format: - # ## [NN.NN.NN] - # Content continues until the next ## header - awk -v ver="${VERSION}" ' - BEGIN { in_section=0 } - /^##[[:space:]]*\[/ { - if ($0 ~ "^##[[:space:]]*\[" ver "\]") { - in_section=1 - print - next - } - if (in_section==1) { exit } - } - { if (in_section==1) print } - ' CHANGELOG.md > "${NOTES_FILE}" || true - - if [ ! -s "${NOTES_FILE}" ]; then - { - echo "## [${VERSION}]" - echo - echo "No matching version section found in CHANGELOG.md." - } > "${NOTES_FILE}" - fi - - echo "Generated release notes: ${NOTES_FILE}" >&2 - wc -l "${NOTES_FILE}" >&2 || true - - - name: Attach ZIP and Release Notes to GitHub Release - if: steps.params.outputs.attach == 'true' - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ steps.params.outputs.tag_name }} - prerelease: ${{ steps.params.outputs.prerelease == 'true' }} - body_path: release-notes-${{ steps.params.outputs.version }}.md - files: | - ${{ steps.params.outputs.zip_name }} - release-notes-${{ steps.params.outputs.version }}.md - fail_on_unmatched_files: true