From 265ec252e9cb1c647dc37131dbeadfac2dcd57ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:26:12 +0000 Subject: [PATCH 1/5] Initial plan 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 2/5] 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 From e8d91a717cdcdbb4b4b65c93c8ced78d8da31053 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:35:11 +0000 Subject: [PATCH 3/5] Add comprehensive scripts documentation - Create scripts/README.md with detailed documentation - Document all script categories and their usage - Add examples and best practices - Include CI integration information Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com> --- scripts/README.md | 288 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 scripts/README.md diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..16c77db --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,288 @@ + + +# Scripts Directory + +This directory contains automation scripts for repository management, validation, +and release processes for the Moko-Cassiopeia Joomla template. + +## Directory Structure + +``` +scripts/ +├── fix/ # Scripts that automatically fix common issues +├── lib/ # Shared library functions +├── release/ # Release automation scripts +├── run/ # Execution and testing scripts +└── validate/ # Validation and linting scripts +``` + +## Library Files (`lib/`) + +### `common.sh` +Core utilities used by all scripts: +- Environment normalization +- Logging functions (`log_info`, `log_warn`, `log_error`, `die`) +- Validation helpers (`assert_file_exists`, `assert_dir_exists`) +- JSON utilities (`json_escape`, `json_output`) +- Path helpers (`script_root`, `normalize_path`) + +Usage: +```bash +. "$(dirname "$0")/../lib/common.sh" +log_info "Starting process..." +``` + +### `joomla_manifest.sh` +Joomla manifest parsing utilities: +- `find_manifest ` - Find primary Joomla manifest XML +- `get_manifest_version ` - Extract version from manifest +- `get_manifest_name ` - Extract extension name +- `get_manifest_type ` - Extract extension type + +Usage: +```bash +. "${SCRIPT_DIR}/lib/joomla_manifest.sh" +MANIFEST="$(find_manifest src)" +VERSION="$(get_manifest_version "${MANIFEST}")" +``` + +### `logging.sh` +Enhanced logging with structured output: +- Colored output support (when in terminal) +- Log levels: `log_debug`, `log_success`, `log_step` +- Structured logging: `log_kv`, `log_item`, `log_section` + +Usage: +```bash +. "${SCRIPT_DIR}/lib/logging.sh" +log_section "Starting validation" +log_kv "Version" "${VERSION}" +log_success "All checks passed" +``` + +## Validation Scripts (`validate/`) + +These scripts validate repository structure, code quality, and compliance. +They are typically run in CI pipelines. + +### `manifest.sh` +Validates Joomla manifest XML structure and required fields. + +### `version_alignment.sh` +Checks that manifest version is documented in CHANGELOG.md. + +### `php_syntax.sh` +Validates PHP syntax using `php -l` on all PHP files. + +### `xml_wellformed.sh` +Validates that all XML files are well-formed. + +### `tabs.sh` +Detects tab characters in source files (enforces spaces). + +### `paths.sh` +Detects Windows-style path separators (backslashes). + +### `no_secrets.sh` +Scans for accidentally committed secrets and credentials. + +### `license_headers.sh` +Checks that source files contain SPDX license identifiers. + +### `language_structure.sh` +Validates Joomla language directory structure and INI files. + +### `changelog.sh` +Validates CHANGELOG.md structure and version entries. + +## Fix Scripts (`fix/`) + +These scripts automatically fix common issues detected by validation scripts. + +### `tabs.sh` +Replaces tab characters with spaces in YAML files. + +Usage: +```bash +./scripts/fix/tabs.sh +``` + +### `paths.sh` +Normalizes Windows-style path separators to forward slashes. + +Usage: +```bash +./scripts/fix/paths.sh [directory] +``` + +### `versions.sh` +Updates version numbers across repository files. + +Usage: +```bash +./scripts/fix/versions.sh +``` + +Example: +```bash +./scripts/fix/versions.sh 3.5.0 +``` + +Updates: +- Manifest XML `` tag +- `package.json` version field +- Version references in README.md + +## Release Scripts (`release/`) + +Scripts for release automation and version management. + +### `update_changelog.sh` +Inserts a new version entry in CHANGELOG.md. + +Usage: +```bash +./scripts/release/update_changelog.sh +``` + +Example: +```bash +./scripts/release/update_changelog.sh 03.05.00 +``` + +### `update_dates.sh` +Normalizes release dates across manifests and CHANGELOG. + +Usage: +```bash +./scripts/release/update_dates.sh +``` + +Example: +```bash +./scripts/release/update_dates.sh 2025-01-15 03.05.00 +``` + +## Run Scripts (`run/`) + +Execution and testing scripts. + +### `smoke_test.sh` +Runs basic smoke tests to verify repository health: +- Repository structure validation +- Manifest validation +- Version alignment checks +- PHP syntax validation + +Usage: +```bash +./scripts/run/smoke_test.sh +``` + +Example output: +``` +INFO: Running smoke tests for Moko-Cassiopeia repository +INFO: Checking repository structure... +INFO: ✓ Repository structure valid +INFO: Checking Joomla manifest... +INFO: Found manifest: src/templates/templateDetails.xml +INFO: Extension: moko-cassiopeia (template) v03.05.00 +INFO: ✓ Manifest validation passed +INFO: ========================================= +INFO: Smoke tests completed successfully +INFO: ========================================= +``` + +## Best Practices + +### Writing New Scripts + +1. **Use the library functions**: + ```bash + #!/usr/bin/env bash + set -euo pipefail + + SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" + . "${SCRIPT_DIR}/lib/common.sh" + ``` + +2. **Include proper headers**: + - Copyright notice + - SPDX license identifier + - FILE INFORMATION section with DEFGROUP, INGROUP, PATH, VERSION, BRIEF + +3. **Follow error handling conventions**: + ```bash + [ -f "${FILE}" ] || die "File not found: ${FILE}" + require_cmd python3 + ``` + +4. **Use structured output**: + ```bash + log_info "Starting process..." + log_success "Process completed" + ``` + +5. **Make scripts executable**: + ```bash + chmod +x scripts/new-script.sh + ``` + +### Testing Scripts Locally + +Run validation scripts before committing: +```bash +./scripts/validate/manifest.sh +./scripts/validate/php_syntax.sh +./scripts/validate/tabs.sh +``` + +Run smoke tests: +```bash +./scripts/run/smoke_test.sh +``` + +### CI Integration + +Scripts are automatically executed in GitHub Actions workflows: +- `.github/workflows/ci.yml` - Continuous integration +- `.github/workflows/repo_health.yml` - Repository health checks + +## Version History + +| Version | Date | Description | +| ------- | ---------- | ------------------------------------- | +| 01.00.00 | 2025-01-03 | Initial scripts documentation created | + +## Metadata + +- **Document:** scripts/README.md +- **Repository:** https://github.com/mokoconsulting-tech/moko-cassiopeia +- **Version:** 01.00.00 +- **Status:** Active From 4bff6c4ad70b66bc60c4f1e294cb03c3db1d9336 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:36:49 +0000 Subject: [PATCH 4/5] Add validate_all.sh script for comprehensive testing - Create validate_all.sh to run all validation checks - Provide clear summary with pass/fail counts - Update scripts/README.md with validate_all documentation - Fix logging.sh path resolution for sourcing Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com> --- scripts/README.md | 39 +++++++++- scripts/lib/logging.sh | 10 ++- scripts/run/validate_all.sh | 141 ++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 3 deletions(-) create mode 100755 scripts/run/validate_all.sh diff --git a/scripts/README.md b/scripts/README.md index 16c77db..69be32f 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -219,6 +219,38 @@ INFO: Smoke tests completed successfully INFO: ========================================= ``` +### `validate_all.sh` +Runs all validation scripts and provides a comprehensive report: +- Executes all required validation checks +- Executes all optional validation checks +- Provides colored output with pass/fail indicators +- Returns summary with counts + +Usage: +```bash +./scripts/run/validate_all.sh +``` + +Example output: +``` +=== Repository Validation Suite === +INFO: Running all validation checks... + +=== Required Checks === +[SUCCESS] ✓ manifest +[SUCCESS] ✓ xml_wellformed + +=== Optional Checks === +[SUCCESS] ✓ no_secrets +[SUCCESS] ✓ php_syntax +WARN: ✗ tabs (warnings/issues found) + +=== Validation Summary === + Required checks passed: 2/2 + Optional checks passed: 2/8 +[SUCCESS] SUCCESS: All required checks passed +``` + ## Best Practices ### Writing New Scripts @@ -256,7 +288,12 @@ INFO: ========================================= ### Testing Scripts Locally -Run validation scripts before committing: +Run all validation scripts: +```bash +./scripts/run/validate_all.sh +``` + +Run individual validation scripts: ```bash ./scripts/validate/manifest.sh ./scripts/validate/php_syntax.sh diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh index 6b83ce7..e2e4cb2 100755 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -35,8 +35,14 @@ set -eu -# Resolve script directory properly -SCRIPT_LIB_DIR="$(cd "$(dirname "$0")" && pwd)" +# 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" diff --git a/scripts/run/validate_all.sh b/scripts/run/validate_all.sh new file mode 100755 index 0000000..d121c9b --- /dev/null +++ b/scripts/run/validate_all.sh @@ -0,0 +1,141 @@ +#!/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.Run +# INGROUP: Repository.Validation +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/run/validate_all.sh +# VERSION: 01.00.00 +# BRIEF: Run all validation scripts and report results +# NOTE: Helpful for developers to run all checks before committing +# ============================================================================ + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +. "${SCRIPT_DIR}/lib/common.sh" +. "${SCRIPT_DIR}/lib/logging.sh" + +# ---------------------------------------------------------------------------- +# Configuration +# ---------------------------------------------------------------------------- + +REQUIRED_CHECKS=( + "manifest" + "xml_wellformed" +) + +OPTIONAL_CHECKS=( + "changelog" + "language_structure" + "license_headers" + "no_secrets" + "paths" + "php_syntax" + "tabs" + "version_alignment" +) + +# ---------------------------------------------------------------------------- +# Run validations +# ---------------------------------------------------------------------------- + +log_section "Repository Validation Suite" +log_info "Running all validation checks..." +log_separator + +required_passed=0 +required_failed=0 +optional_passed=0 +optional_failed=0 + +# Required checks +log_section "Required Checks" +for check in "${REQUIRED_CHECKS[@]}"; do + script="${SCRIPT_DIR}/validate/${check}.sh" + if [ ! -f "${script}" ]; then + log_error "Script not found: ${script}" + required_failed=$((required_failed + 1)) + continue + fi + + log_step "Running: ${check}" + if "${script}" >/dev/null 2>&1; then + log_success "✓ ${check}" + required_passed=$((required_passed + 1)) + else + log_error "✗ ${check} (FAILED)" + required_failed=$((required_failed + 1)) + fi +done + +echo "" + +# Optional checks +log_section "Optional Checks" +for check in "${OPTIONAL_CHECKS[@]}"; do + script="${SCRIPT_DIR}/validate/${check}.sh" + if [ ! -f "${script}" ]; then + log_warn "Script not found: ${script}" + continue + fi + + log_step "Running: ${check}" + if "${script}" >/dev/null 2>&1; then + log_success "✓ ${check}" + optional_passed=$((optional_passed + 1)) + else + log_warn "✗ ${check} (warnings/issues found)" + optional_failed=$((optional_failed + 1)) + fi +done + +# ---------------------------------------------------------------------------- +# Summary +# ---------------------------------------------------------------------------- + +echo "" +log_separator +log_section "Validation Summary" +log_separator + +log_kv "Required checks passed" "${required_passed}/${#REQUIRED_CHECKS[@]}" +log_kv "Required checks failed" "${required_failed}" +log_kv "Optional checks passed" "${optional_passed}/${#OPTIONAL_CHECKS[@]}" +log_kv "Optional checks with issues" "${optional_failed}" + +log_separator + +if [ "${required_failed}" -gt 0 ]; then + log_error "FAILED: ${required_failed} required check(s) failed" + exit 1 +else + log_success "SUCCESS: All required checks passed" + if [ "${optional_failed}" -gt 0 ]; then + log_warn "Note: ${optional_failed} optional check(s) found issues" + fi + exit 0 +fi From 62f532533805e225f408721cfbd3c20a267397e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:39:22 +0000 Subject: [PATCH 5/5] Address code review feedback - Add version component validation in versions.sh - Add verbose mode to validate_all.sh for debugging - Update documentation with verbose mode usage - Improve error messages with hints to use verbose mode Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com> --- scripts/README.md | 6 +++-- scripts/fix/versions.sh | 20 ++++++++++------ scripts/run/validate_all.sh | 47 +++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/scripts/README.md b/scripts/README.md index 69be32f..41a1edc 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -225,10 +225,12 @@ Runs all validation scripts and provides a comprehensive report: - Executes all optional validation checks - Provides colored output with pass/fail indicators - Returns summary with counts +- Supports verbose mode for detailed output Usage: ```bash -./scripts/run/validate_all.sh +./scripts/run/validate_all.sh # Standard mode +./scripts/run/validate_all.sh -v # Verbose mode (shows all output) ``` Example output: @@ -243,7 +245,7 @@ INFO: Running all validation checks... === Optional Checks === [SUCCESS] ✓ no_secrets [SUCCESS] ✓ php_syntax -WARN: ✗ tabs (warnings/issues found) +WARN: ✗ tabs (warnings/issues found - run with -v for details) === Validation Summary === Required checks passed: 2/2 diff --git a/scripts/fix/versions.sh b/scripts/fix/versions.sh index 581536c..491bf3c 100755 --- a/scripts/fix/versions.sh +++ b/scripts/fix/versions.sh @@ -132,13 +132,19 @@ fi # Update README.md version references if [ -f "README.md" ]; then -# Look for version references in format VERSION: XX.XX.XX -if grep -q 'VERSION: [0-9]' README.md; then -# Convert to zero-padded format if needed -PADDED_VERSION="$(printf '%s' "${VERSION}" | awk -F. '{printf "%02d.%02d.%02d", $1, $2, $3}')" -sed_inplace "s|VERSION: [0-9]{2}\.[0-9]{2}\.[0-9]{2}|VERSION: ${PADDED_VERSION}|g" README.md -log_info "✓ Updated README.md version references" -fi + # Look for version references in format VERSION: XX.XX.XX + if grep -q 'VERSION: [0-9]' README.md; then + # Validate version format has 3 components + component_count=$(echo "${VERSION}" | awk -F. '{print NF}') + if [ "${component_count}" -eq 3 ]; then + # Convert to zero-padded format + PADDED_VERSION="$(printf '%s' "${VERSION}" | awk -F. '{printf "%02d.%02d.%02d", $1, $2, $3}')" + sed_inplace "s|VERSION: [0-9]{2}\.[0-9]{2}\.[0-9]{2}|VERSION: ${PADDED_VERSION}|g" README.md + log_info "✓ Updated README.md version references" + else + log_warn "Version format invalid for padding, skipping README.md update" + fi + fi fi log_info "=========================================" diff --git a/scripts/run/validate_all.sh b/scripts/run/validate_all.sh index d121c9b..515cd6f 100755 --- a/scripts/run/validate_all.sh +++ b/scripts/run/validate_all.sh @@ -43,6 +43,13 @@ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" # Configuration # ---------------------------------------------------------------------------- +VERBOSE="${1:-}" +if [ "${VERBOSE}" = "-v" ] || [ "${VERBOSE}" = "--verbose" ]; then + VERBOSE="true" +else + VERBOSE="false" +fi + REQUIRED_CHECKS=( "manifest" "xml_wellformed" @@ -83,12 +90,22 @@ for check in "${REQUIRED_CHECKS[@]}"; do fi log_step "Running: ${check}" - if "${script}" >/dev/null 2>&1; then - log_success "✓ ${check}" - required_passed=$((required_passed + 1)) + if [ "${VERBOSE}" = "true" ]; then + if "${script}"; then + log_success "✓ ${check}" + required_passed=$((required_passed + 1)) + else + log_error "✗ ${check} (FAILED)" + required_failed=$((required_failed + 1)) + fi else - log_error "✗ ${check} (FAILED)" - required_failed=$((required_failed + 1)) + if "${script}" >/dev/null 2>&1; then + log_success "✓ ${check}" + required_passed=$((required_passed + 1)) + else + log_error "✗ ${check} (FAILED - run with -v for details)" + required_failed=$((required_failed + 1)) + fi fi done @@ -104,12 +121,22 @@ for check in "${OPTIONAL_CHECKS[@]}"; do fi log_step "Running: ${check}" - if "${script}" >/dev/null 2>&1; then - log_success "✓ ${check}" - optional_passed=$((optional_passed + 1)) + if [ "${VERBOSE}" = "true" ]; then + if "${script}"; then + log_success "✓ ${check}" + optional_passed=$((optional_passed + 1)) + else + log_warn "✗ ${check} (warnings/issues found)" + optional_failed=$((optional_failed + 1)) + fi else - log_warn "✗ ${check} (warnings/issues found)" - optional_failed=$((optional_failed + 1)) + if "${script}" >/dev/null 2>&1; then + log_success "✓ ${check}" + optional_passed=$((optional_passed + 1)) + else + log_warn "✗ ${check} (warnings/issues found - run with -v for details)" + optional_failed=$((optional_failed + 1)) + fi fi done