From ef039cf91f59cf38963883e1bddf06630343c7a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:33:31 +0000 Subject: [PATCH] Implement core script improvements - Add joomla_manifest.sh library with manifest parsing functions - Add smoke_test.sh for repository validation - Add versions.sh for version management - Add JSON utilities to common.sh - Fix logging.sh with proper enhanced logging functions - Make all scripts executable Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com> --- scripts/fix/paths.sh | 0 scripts/fix/tabs.sh | 0 scripts/fix/versions.sh | 150 +++++++++++++++++++ scripts/lib/common.sh | 27 ++++ scripts/lib/joomla_manifest.sh | 193 +++++++++++++++++++++++++ scripts/lib/logging.sh | 146 +++++++++---------- scripts/release/update_changelog.sh | 0 scripts/release/update_dates.sh | 0 scripts/run/smoke_test.sh | 146 +++++++++++++++++++ scripts/validate/changelog.sh | 0 scripts/validate/language_structure.sh | 0 scripts/validate/license_headers.sh | 0 scripts/validate/manifest.sh | 0 scripts/validate/no_secrets.sh | 0 scripts/validate/paths.sh | 0 scripts/validate/php_syntax.sh | 0 scripts/validate/tabs.sh | 0 scripts/validate/version_alignment.sh | 0 scripts/validate/xml_wellformed.sh | 0 19 files changed, 585 insertions(+), 77 deletions(-) mode change 100644 => 100755 scripts/fix/paths.sh mode change 100644 => 100755 scripts/fix/tabs.sh mode change 100644 => 100755 scripts/fix/versions.sh mode change 100644 => 100755 scripts/lib/common.sh mode change 100644 => 100755 scripts/lib/joomla_manifest.sh mode change 100644 => 100755 scripts/lib/logging.sh mode change 100644 => 100755 scripts/release/update_changelog.sh mode change 100644 => 100755 scripts/release/update_dates.sh mode change 100644 => 100755 scripts/run/smoke_test.sh mode change 100644 => 100755 scripts/validate/changelog.sh mode change 100644 => 100755 scripts/validate/language_structure.sh mode change 100644 => 100755 scripts/validate/license_headers.sh mode change 100644 => 100755 scripts/validate/manifest.sh mode change 100644 => 100755 scripts/validate/no_secrets.sh mode change 100644 => 100755 scripts/validate/paths.sh mode change 100644 => 100755 scripts/validate/php_syntax.sh mode change 100644 => 100755 scripts/validate/tabs.sh mode change 100644 => 100755 scripts/validate/version_alignment.sh mode change 100644 => 100755 scripts/validate/xml_wellformed.sh diff --git a/scripts/fix/paths.sh b/scripts/fix/paths.sh old mode 100644 new mode 100755 diff --git a/scripts/fix/tabs.sh b/scripts/fix/tabs.sh old mode 100644 new mode 100755 diff --git a/scripts/fix/versions.sh b/scripts/fix/versions.sh old mode 100644 new mode 100755 index e69de29..581536c --- a/scripts/fix/versions.sh +++ b/scripts/fix/versions.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash + +# ============================================================================ +# 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 (./LICENSE.md). +# ============================================================================ + +# ============================================================================ +# FILE INFORMATION +# ============================================================================ +# DEFGROUP: Script.Fix +# INGROUP: Version.Management +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/fix/versions.sh +# VERSION: 01.00.00 +# BRIEF: Update version numbers across repository files +# NOTE: Updates manifest, package.json, and other version references +# ============================================================================ + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +. "${SCRIPT_DIR}/lib/common.sh" + +# ---------------------------------------------------------------------------- +# Usage and validation +# ---------------------------------------------------------------------------- + +usage() { +cat <<-USAGE +Usage: $0 + +Update version numbers across repository files. + +Arguments: + VERSION Semantic version in format X.Y.Z (e.g., 3.5.0) + +Examples: + $0 3.5.0 + $0 1.2.3 +USAGE +exit 1 +} + +validate_version() { +local v="$1" +if ! printf '%s' "$v" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then +die "Invalid version format: $v (expected X.Y.Z)" +fi +} + +# ---------------------------------------------------------------------------- +# Main +# ---------------------------------------------------------------------------- + +[ $# -eq 1 ] || usage + +VERSION="$1" +validate_version "${VERSION}" + +log_info "Updating version to: ${VERSION}" + +# Source Joomla manifest utilities +. "${SCRIPT_DIR}/lib/joomla_manifest.sh" + +# Find and update manifest +MANIFEST="$(find_manifest src)" +log_info "Updating manifest: ${MANIFEST}" + +# Cross-platform sed helper +sed_inplace() { +local expr="$1" +local file="$2" + +if sed --version >/dev/null 2>&1; then +sed -i -E "${expr}" "${file}" +else +sed -i '' -E "${expr}" "${file}" +fi +} + +# Update version in manifest XML +if grep -q '' "${MANIFEST}"; then +sed_inplace "s|[^<]*|${VERSION}|g" "${MANIFEST}" +log_info "✓ Updated manifest version" +else +log_warn "No tag found in manifest" +fi + +# Update package.json if it exists +if [ -f "package.json" ]; then +if command -v python3 >/dev/null 2>&1; then +python3 - < +# +# 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 (./LICENSE.md). +# ============================================================================ + +# ============================================================================ +# FILE INFORMATION +# ============================================================================ +# DEFGROUP: Script.Library +# INGROUP: Joomla.Manifest +# REPO: https://github.com/mokoconsulting-tech +# PATH: /scripts/lib/joomla_manifest.sh +# VERSION: 01.00.00 +# BRIEF: Joomla manifest parsing and validation utilities +# NOTE: Provides reusable functions for working with Joomla extension manifests +# ============================================================================ + +set -eu + +# Resolve script directory properly - works when sourced +if [ -n "${SCRIPT_DIR:-}" ]; then + # Already set by caller + SCRIPT_LIB_DIR="${SCRIPT_DIR}/lib" +else + # Determine from this file's location + SCRIPT_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" +fi + +# Shared utilities +. "${SCRIPT_LIB_DIR}/common.sh" + +# ---------------------------------------------------------------------------- +# Manifest discovery +# ---------------------------------------------------------------------------- + +# Find the primary Joomla manifest in the given directory +# Usage: find_manifest +# Returns: path to manifest file or exits with error +find_manifest() { +local src_dir="${1:-src}" + +[ -d "${src_dir}" ] || die "Source directory missing: ${src_dir}" + +# Candidate discovery policy: prefer explicit known names +local candidates="" + +# Template +if [ -f "${src_dir}/templateDetails.xml" ]; then +candidates="${src_dir}/templateDetails.xml" +fi + +# Package +if [ -z "${candidates}" ]; then +candidates="$(find "${src_dir}" -maxdepth 4 -type f -name 'pkg_*.xml' 2>/dev/null | head -1 || true)" +fi + +# Component +if [ -z "${candidates}" ]; then +candidates="$(find "${src_dir}" -maxdepth 4 -type f -name 'com_*.xml' 2>/dev/null | head -1 || true)" +fi + +# Module +if [ -z "${candidates}" ]; then +candidates="$(find "${src_dir}" -maxdepth 4 -type f -name 'mod_*.xml' 2>/dev/null | head -1 || true)" +fi + +# Plugin +if [ -z "${candidates}" ]; then +candidates="$(find "${src_dir}" -maxdepth 6 -type f -name 'plg_*.xml' 2>/dev/null | head -1 || true)" +fi + +# Fallback: any XML containing +if [ -z "${candidates}" ]; then +candidates="$(grep -Rsl --include='*.xml' '/dev/null | head -1 || true)" +fi + +[ -n "${candidates}" ] || die "No Joomla manifest XML found under ${src_dir}" +[ -s "${candidates}" ] || die "Manifest is empty: ${candidates}" + +printf '%s\n' "${candidates}" +} + +# ---------------------------------------------------------------------------- +# Manifest parsing +# ---------------------------------------------------------------------------- + +# Extract version from manifest XML +# Usage: get_manifest_version +# Returns: version string or exits with error +get_manifest_version() { + local manifest="$1" + + [ -f "${manifest}" ] || die "Manifest not found: ${manifest}" + + require_cmd python3 + + python3 - "${manifest}" <<'PY' +import sys +import xml.etree.ElementTree as ET + +manifest_path = sys.argv[1] + +try: + tree = ET.parse(manifest_path) + root = tree.getroot() + version_el = root.find("version") + if version_el is not None and version_el.text: + print(version_el.text.strip()) + sys.exit(0) +except Exception: + pass + +sys.exit(1) +PY +} + +# Extract extension name from manifest XML +# Usage: get_manifest_name +# Returns: name string or exits with error +get_manifest_name() { + local manifest="$1" + + [ -f "${manifest}" ] || die "Manifest not found: ${manifest}" + + require_cmd python3 + + python3 - "${manifest}" <<'PY' +import sys +import xml.etree.ElementTree as ET + +manifest_path = sys.argv[1] + +try: + tree = ET.parse(manifest_path) + root = tree.getroot() + name_el = root.find("name") + if name_el is not None and name_el.text: + print(name_el.text.strip()) + sys.exit(0) +except Exception: + pass + +sys.exit(1) +PY +} + +# Extract extension type from manifest XML +# Usage: get_manifest_type +# Returns: type string (template, component, module, plugin, etc.) or exits with error +get_manifest_type() { + local manifest="$1" + + [ -f "${manifest}" ] || die "Manifest not found: ${manifest}" + + require_cmd python3 + + python3 - "${manifest}" <<'PY' +import sys +import xml.etree.ElementTree as ET + +manifest_path = sys.argv[1] + +try: + tree = ET.parse(manifest_path) + root = tree.getroot() + ext_type = root.attrib.get("type", "").strip().lower() + if ext_type: + print(ext_type) + sys.exit(0) +except Exception: + pass + +sys.exit(1) +PY +} diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh old mode 100644 new mode 100755 index 66820bf..6b83ce7 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -25,96 +25,88 @@ # FILE INFORMATION # ============================================================================ # DEFGROUP: Script.Library -# INGROUP: RepoHealth +# INGROUP: Logging # REPO: https://github.com/mokoconsulting-tech -# PATH: /scripts/lib/find_files.sh +# PATH: /scripts/lib/logging.sh # VERSION: 01.00.00 -# BRIEF: Find files by glob patterns with standard ignore rules for CI checks -# NOTE: +# BRIEF: Enhanced logging utilities with structured output support +# NOTE: Provides colored output, log levels, and structured logging # ============================================================================ set -eu +# Resolve script directory properly +SCRIPT_LIB_DIR="$(cd "$(dirname "$0")" && pwd)" + # Shared utilities -. "$(dirname "$0")/common.sh" +. "${SCRIPT_LIB_DIR}/common.sh" # ---------------------------------------------------------------------------- -# Purpose: -# - Provide a consistent, reusable file discovery primitive for repo scripts. -# - Support multiple glob patterns. -# - Apply standard ignore rules to reduce noise (vendor, node_modules, .git). -# - Output one path per line, relative to repo root. -# -# Usage: -# ./scripts/lib/find_files.sh [ ...] -# -# Examples: -# ./scripts/lib/find_files.sh "*.yml" "*.yaml" -# ./scripts/lib/find_files.sh "src/**/*.php" "tests/**/*.php" +# Color codes (if terminal supports it) # ---------------------------------------------------------------------------- -ROOT="$(script_root)" - -if [ "${1:-}" = "" ]; then - die "Usage: $0 [ ...]" -fi - -require_cmd find -require_cmd sed - -# Standard excludes (pragmatic defaults for CI) -# Note: Keep these broad to avoid scanning generated or third-party content. -EXCLUDES=' - -path "*/.git/*" -o - -path "*/.github/*/node_modules/*" -o - -path "*/node_modules/*" -o - -path "*/vendor/*" -o - -path "*/dist/*" -o - -path "*/build/*" -o - -path "*/cache/*" -o - -path "*/tmp/*" -o - -path "*/.tmp/*" -o - -path "*/.cache/*" -' - -# Convert a glob (bash-like) to a find -path pattern. -# - Supports ** for "any directories" by translating to * -# - Ensures leading */ so patterns apply anywhere under repo root -glob_to_find_path() { - g="$1" - - # normalize path separators for WSL/CI compatibility - g="$(normalize_path "$g")" - - # translate ** to * (find -path uses shell glob semantics) - g="$(printf '%s' "$g" | sed 's|\*\*|*|g')" - - case "$g" in - /*) printf '%s\n' "$g" ;; - *) printf '%s\n' "*/$g" ;; - esac +# Check if we're in a terminal and colors are supported +use_colors() { + [ -t 1 ] && [ "${CI:-false}" != "true" ] } -# Build a single find invocation that ORs all patterns. -# Shell portability note: avoid arrays; build an expression string. -PAT_EXPR="" -for GLOB in "$@"; do - P="$(glob_to_find_path "$GLOB")" - if [ -z "$PAT_EXPR" ]; then - PAT_EXPR="-path \"$P\"" - else - PAT_EXPR="$PAT_EXPR -o -path \"$P\"" +if use_colors; then + COLOR_RESET='\033[0m' + COLOR_RED='\033[0;31m' + COLOR_YELLOW='\033[0;33m' + COLOR_GREEN='\033[0;32m' + COLOR_BLUE='\033[0;34m' + COLOR_CYAN='\033[0;36m' +else + COLOR_RESET='' + COLOR_RED='' + COLOR_YELLOW='' + COLOR_GREEN='' + COLOR_BLUE='' + COLOR_CYAN='' +fi + +# ---------------------------------------------------------------------------- +# Enhanced logging functions +# ---------------------------------------------------------------------------- + +log_debug() { + if [ "${DEBUG:-false}" = "true" ]; then + printf '%b[DEBUG]%b %s\n' "${COLOR_CYAN}" "${COLOR_RESET}" "$*" fi -done +} -# Execute find and emit relative paths. -# - Use eval to apply the constructed predicate string safely as a single expression. -# - We scope to files only. -# - We prune excluded directories. -cd "$ROOT" +log_success() { + printf '%b[SUCCESS]%b %s\n' "${COLOR_GREEN}" "${COLOR_RESET}" "$*" +} + +log_step() { + printf '%b[STEP]%b %s\n' "${COLOR_BLUE}" "${COLOR_RESET}" "$*" +} + +# ---------------------------------------------------------------------------- +# Structured logging +# ---------------------------------------------------------------------------- + +# Log a key-value pair +log_kv() { + local key="$1" + local value="$2" + printf ' %b%s:%b %s\n' "${COLOR_BLUE}" "${key}" "${COLOR_RESET}" "${value}" +} + +# Log a list item +log_item() { + printf ' %b•%b %s\n' "${COLOR_GREEN}" "${COLOR_RESET}" "$*" +} + +# Log a separator line +log_separator() { + printf '%s\n' "=========================================" +} + +# Log a section header +log_section() { + printf '\n%b=== %s ===%b\n' "${COLOR_BLUE}" "$*" "${COLOR_RESET}" +} -# shellcheck disable=SC2086 -eval "find . \\( $EXCLUDES \\) -prune -o -type f \\( $PAT_EXPR \\) -print" \ - | sed 's|^\./||' \ - | sed '/^$/d' \ - | sort -u diff --git a/scripts/release/update_changelog.sh b/scripts/release/update_changelog.sh old mode 100644 new mode 100755 diff --git a/scripts/release/update_dates.sh b/scripts/release/update_dates.sh old mode 100644 new mode 100755 diff --git a/scripts/run/smoke_test.sh b/scripts/run/smoke_test.sh old mode 100644 new mode 100755 index e69de29..d481ee5 --- a/scripts/run/smoke_test.sh +++ b/scripts/run/smoke_test.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash + +# ============================================================================ +# 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 (./LICENSE.md). +# ============================================================================ + +# ============================================================================ +# FILE INFORMATION +# ============================================================================ +# DEFGROUP: Script.Test +# INGROUP: Repository.Validation +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/run/smoke_test.sh +# VERSION: 01.00.00 +# BRIEF: Basic smoke tests to verify repository structure and manifest validity +# NOTE: Quick validation checks for essential repository components +# ============================================================================ + +set -euo pipefail + +# Source common utilities +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +. "${SCRIPT_DIR}/lib/common.sh" + +log_info "Running smoke tests for Moko-Cassiopeia repository" + +# ---------------------------------------------------------------------------- +# Test: Repository structure +# ---------------------------------------------------------------------------- + +log_info "Checking repository structure..." + +assert_dir_exists "src" +assert_file_exists "README.md" +assert_file_exists "CHANGELOG.md" +assert_file_exists "LICENSE" +assert_file_exists "CONTRIBUTING.md" + +log_info "✓ Repository structure valid" + +# ---------------------------------------------------------------------------- +# Test: Manifest validation +# ---------------------------------------------------------------------------- + +log_info "Checking Joomla manifest..." + +. "${SCRIPT_DIR}/lib/joomla_manifest.sh" + +MANIFEST="$(find_manifest src)" +log_info "Found manifest: ${MANIFEST}" + +VERSION="$(get_manifest_version "${MANIFEST}")" +NAME="$(get_manifest_name "${MANIFEST}")" +TYPE="$(get_manifest_type "${MANIFEST}")" + +log_info "Extension: ${NAME} (${TYPE}) v${VERSION}" + +# Verify manifest is well-formed XML +require_cmd python3 +python3 - "${MANIFEST}" <<'PY' +import sys +import xml.etree.ElementTree as ET + +manifest_path = sys.argv[1] + +try: + tree = ET.parse(manifest_path) + root = tree.getroot() + if root.tag != "extension": + print(f"ERROR: Root element must be , got <{root.tag}>") + sys.exit(1) + print("✓ Manifest XML is well-formed") +except Exception as e: + print(f"ERROR: Failed to parse manifest: {e}") + sys.exit(1) +PY + +log_info "✓ Manifest validation passed" + +# ---------------------------------------------------------------------------- +# Test: Version alignment +# ---------------------------------------------------------------------------- + +log_info "Checking version alignment..." + +if [ ! -f "CHANGELOG.md" ]; then +log_warn "CHANGELOG.md not found, skipping version alignment check" +else +if grep -q "## \[${VERSION}\]" CHANGELOG.md; then +log_info "✓ Version ${VERSION} found in CHANGELOG.md" +else +log_warn "Version ${VERSION} not found in CHANGELOG.md" +fi +fi + +# ---------------------------------------------------------------------------- +# Test: Critical files syntax +# ---------------------------------------------------------------------------- + +log_info "Checking PHP syntax..." + +if command -v php >/dev/null 2>&1; then +php_errors=0 +while IFS= read -r -d '' f; do +if ! php -l "$f" >/dev/null 2>&1; then +log_error "PHP syntax error in: $f" +php_errors=$((php_errors + 1)) +fi +done < <(find src -type f -name '*.php' -print0 2>/dev/null) + +if [ "${php_errors}" -eq 0 ]; then +log_info "✓ PHP syntax validation passed" +else +die "Found ${php_errors} PHP syntax errors" +fi +else +log_warn "PHP not available, skipping PHP syntax check" +fi + +# ---------------------------------------------------------------------------- +# Summary +# ---------------------------------------------------------------------------- + +log_info "=========================================" +log_info "Smoke tests completed successfully" +log_info "Extension: ${NAME}" +log_info "Version: ${VERSION}" +log_info "Type: ${TYPE}" +log_info "=========================================" diff --git a/scripts/validate/changelog.sh b/scripts/validate/changelog.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/language_structure.sh b/scripts/validate/language_structure.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/license_headers.sh b/scripts/validate/license_headers.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/manifest.sh b/scripts/validate/manifest.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/no_secrets.sh b/scripts/validate/no_secrets.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/paths.sh b/scripts/validate/paths.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/php_syntax.sh b/scripts/validate/php_syntax.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/tabs.sh b/scripts/validate/tabs.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/version_alignment.sh b/scripts/validate/version_alignment.sh old mode 100644 new mode 100755 diff --git a/scripts/validate/xml_wellformed.sh b/scripts/validate/xml_wellformed.sh old mode 100644 new mode 100755