diff --git a/scripts/ENTERPRISE.md b/scripts/ENTERPRISE.md new file mode 100644 index 0000000..a89e28a --- /dev/null +++ b/scripts/ENTERPRISE.md @@ -0,0 +1,583 @@ + + +# Enterprise Standards for Scripts + +This document defines the enterprise-grade standards and best practices +implemented across all automation scripts in this repository. + +## Table of Contents + +- [Overview](#overview) +- [Core Principles](#core-principles) +- [Script Structure](#script-structure) +- [Error Handling](#error-handling) +- [Logging and Observability](#logging-and-observability) +- [Security Standards](#security-standards) +- [Dependency Management](#dependency-management) +- [Exit Codes](#exit-codes) +- [Documentation Requirements](#documentation-requirements) +- [Testing and Validation](#testing-and-validation) +- [Operational Considerations](#operational-considerations) + +## Overview + +All scripts in this repository follow enterprise-grade standards to ensure: +- **Reliability**: Predictable behavior in all environments +- **Security**: Protection against vulnerabilities and credential exposure +- **Observability**: Clear logging and error reporting +- **Maintainability**: Consistent patterns and documentation +- **Portability**: Cross-platform compatibility + +## Core Principles + +### 1. Fail Fast, Fail Clearly + +Scripts must fail immediately when encountering errors and provide clear, +actionable error messages. + +```bash +set -euo pipefail # Required at top of all bash scripts +``` + +- `-e`: Exit on first error +- `-u`: Exit on undefined variable reference +- `-o pipefail`: Propagate pipeline failures + +### 2. Zero Assumptions + +- Always validate inputs +- Check for required dependencies +- Verify file/directory existence before access +- Never assume environment state + +### 3. Idempotency Where Possible + +Scripts should be safe to run multiple times without causing harm or +inconsistency. + +### 4. Least Privilege + +Scripts should: +- Never require root unless absolutely necessary +- Use minimal file system permissions +- Validate before modifying files + +## Script Structure + +### Standard Header Template + +Every script must include: + +```bash +#!/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 +# +# [Full license text...] +# ============================================================================ + +# ============================================================================ +# FILE INFORMATION +# ============================================================================ +# DEFGROUP: Script.Category +# INGROUP: Subcategory +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/path/to/script.sh +# VERSION: XX.XX.XX +# BRIEF: One-line description of script purpose +# NOTE: Additional context or usage notes +# ============================================================================ + +set -euo pipefail +``` + +### Usage Function + +User-facing scripts must provide a usage/help function: + +```bash +usage() { +cat <<-USAGE +Usage: $0 [OPTIONS] + +Description of what the script does. + +Options: + -h, --help Show this help message + -v, --verbose Enable verbose output + +Arguments: + ARG1 Description of first argument + ARG2 Description of second argument + +Examples: + $0 example_value + $0 -v example_value + +Exit codes: + 0 - Success + 1 - Error + 2 - Invalid arguments + +USAGE +exit 0 +} +``` + +### Argument Parsing + +```bash +# Parse arguments +if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then + usage +fi + +[ $# -ge 1 ] || usage +``` + +### Library Sourcing + +```bash +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +. "${SCRIPT_DIR}/lib/common.sh" + +# Check dependencies +check_dependencies python3 git +``` + +## Error Handling + +### Error Messages + +Error messages must be: +- **Clear**: Explain what went wrong +- **Actionable**: Tell user how to fix it +- **Contextual**: Include relevant details +- **Verbose**: Provide comprehensive information by default + +```bash +# Bad +die "Error" + +# Good - Verbose with context and solutions +die "Required file not found: ${CONFIG_FILE} + + Current directory: $(pwd) + Expected location: ./config/${CONFIG_FILE} + + To fix: + 1. Run setup script: ./scripts/setup.sh + 2. Or create the file manually: touch config/${CONFIG_FILE} + " +``` + +### Error Output + +- Always show full error output for failed operations +- Include line numbers and file paths +- Show error summaries with troubleshooting steps +- Provide installation guides for missing dependencies + +Example verbose error from validation: +``` +ERROR: PHP syntax validation failed +Files checked: 90 +Files with errors: 2 + +Failed files and errors: + File: src/test.php + Error: Parse error: syntax error, unexpected '}' in src/test.php on line 42 + + File: src/helper.php + Error: Parse error: syntax error, unexpected T_STRING in src/helper.php on line 15 + +To fix: Review and correct the syntax errors in the files listed above. +Run 'php -l ' on individual files for detailed error messages. +``` + +### Validation + +```bash +# Validate inputs +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 +} + +# Check file existence +assert_file_exists "${MANIFEST}" || die "Manifest not found: ${MANIFEST}" + +# Verify directory +assert_dir_exists "${SRC_DIR}" || die "Source directory missing: ${SRC_DIR}" +``` + +## Logging and Observability + +### Logging Functions + +Use standard logging functions from `lib/common.sh`: + +```bash +log_info "Starting process..." # Informational messages +log_warn "Configuration missing" # Warnings (non-fatal) +log_error "Validation failed" # Errors (fatal) +die "Critical error occurred" # Fatal with exit +``` + +### Timestamps + +Include timestamps for audit trails: + +```bash +log_info "Start time: $(log_timestamp)" +# ... work ... +log_info "End time: $(log_timestamp)" +``` + +### Structured Output + +For machine-readable output, use JSON: + +```bash +printf '{"status":"ok","files_checked":%s}\n' "${count}" +``` + +### Progress Reporting + +For long-running operations: + +```bash +log_section "Phase 1: Validation" +log_step "Checking manifests..." +log_success "✓ Manifests valid" +log_kv "Files processed" "${count}" +``` + +## Security Standards + +### 1. No Hardcoded Secrets + +- Never commit credentials +- Use environment variables for sensitive data +- Validate against secret patterns + +### 2. Input Sanitization + +```bash +# Validate user input +if [[ "${input}" =~ [^a-zA-Z0-9._-] ]]; then + die "Invalid input: contains disallowed characters" +fi +``` + +### 3. File Operations + +```bash +# Use explicit paths +FILE="/full/path/to/file" + +# Avoid user-controlled paths without validation +# Validate before rm/mv operations +``` + +### 4. Command Injection Prevention + +```bash +# Use arrays for command arguments +args=("$file1" "$file2") +command "${args[@]}" + +# Quote all variables +grep "${pattern}" "${file}" +``` + +## Dependency Management + +### Required Dependencies Check + +```bash +# At script start +check_dependencies python3 git sed + +# Or inline +require_cmd xmllint || die "xmllint not available" +``` + +### Graceful Degradation + +When optional dependencies are missing: + +```bash +if ! command -v php >/dev/null 2>&1; then + log_warn "PHP not available, skipping syntax check" + exit 0 +fi +``` + +## Exit Codes + +Standard exit codes across all scripts: + +| Code | Meaning | Usage | +|------|---------|-------| +| 0 | Success | All operations completed successfully | +| 1 | Error | Fatal error occurred | +| 2 | Invalid arguments | Bad command-line arguments or usage | + +```bash +# Success +exit 0 + +# Fatal error +die "Error message" # Exits with code 1 + +# Invalid arguments +usage # Exits with code 0 (help shown) +# or +log_error "Invalid argument" +exit 2 +``` + +## Documentation Requirements + +### 1. Script Headers + +Must include: +- Copyright notice +- SPDX license identifier +- FILE INFORMATION section +- Version number +- Brief description + +### 2. Inline Comments + +Use comments for: +- Complex logic explanation +- Why decisions were made (not what code does) +- Security considerations +- Performance notes + +```bash +# Use git ls-files for performance vs. find +files=$(git ls-files '*.yml' '*.yaml') + +# NOTE: Binary detection prevents corrupting image files +if file --mime-type "$f" | grep -q '^application/'; then + continue +fi +``` + +### 3. README Documentation + +Update `scripts/README.md` when: +- Adding new scripts +- Changing script behavior +- Adding new library functions + +## Testing and Validation + +### Self-Testing + +Scripts should validate their own requirements: + +```bash +# Validate environment +[ -d "${SRC_DIR}" ] || die "Source directory not found" + +# Validate configuration +[ -n "${VERSION}" ] || die "VERSION must be set" +``` + +### Script Health Checking + +Use the script health checker to validate all scripts follow standards: + +```bash +./scripts/run/script_health.sh # Check all scripts +./scripts/run/script_health.sh -v # Verbose mode with details +``` + +The health checker validates: +- Copyright headers present +- SPDX license identifiers +- FILE INFORMATION sections +- Error handling (set -euo pipefail) +- Executable permissions + +### Integration Testing + +Run validation suite before commits: + +```bash +./scripts/run/validate_all.sh +``` + +### Smoke Testing + +Basic health checks: + +```bash +./scripts/run/smoke_test.sh +``` + +## Operational Considerations + +### 1. Timeout Handling + +For long-running operations: + +```bash +run_with_timeout 300 long_running_command +``` + +### 2. Cleanup + +Use traps for cleanup: + +```bash +cleanup() { + rm -f "${TEMP_FILE}" +} +trap cleanup EXIT +``` + +### 3. Lock Files + +For singleton operations: + +```bash +LOCK_FILE="/tmp/script.lock" +if [ -f "${LOCK_FILE}" ]; then + die "Script already running (lock file exists)" +fi +touch "${LOCK_FILE}" +trap "rm -f ${LOCK_FILE}" EXIT +``` + +### 4. Signal Handling + +```bash +handle_interrupt() { + log_warn "Interrupted by user" + cleanup + exit 130 +} +trap handle_interrupt INT TERM +``` + +### 5. Dry Run Mode + +For destructive operations: + +```bash +DRY_RUN="${DRY_RUN:-false}" + +if [ "${DRY_RUN}" = "true" ]; then + log_info "DRY RUN: Would execute: $command" +else + "$command" +fi +``` + +## CI/CD Integration + +### Environment Variables + +Scripts should respect: + +```bash +CI="${CI:-false}" # Running in CI +VERBOSE="${VERBOSE:-false}" # Verbose output +DEBUG="${DEBUG:-false}" # Debug mode +``` + +### CI-Specific Behavior + +```bash +if is_ci; then + # CI-specific settings + set -x # Echo commands for debugging +fi +``` + +### Job Summaries + +For GitHub Actions: + +```bash +if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then + echo "### Validation Results" >> "$GITHUB_STEP_SUMMARY" + echo "Status: PASSED" >> "$GITHUB_STEP_SUMMARY" +fi +``` + +## Review Checklist + +Before committing new or modified scripts: + +- [ ] Includes proper copyright header +- [ ] Uses `set -euo pipefail` +- [ ] Has usage/help function (if user-facing) +- [ ] Validates all inputs +- [ ] Checks dependencies with `check_dependencies` +- [ ] Uses structured logging (`log_info`, `log_error`, etc.) +- [ ] Includes timestamps for audit trails +- [ ] Returns appropriate exit codes (0=success, 1=error, 2=invalid args) +- [ ] Includes inline comments for complex logic +- [ ] Documented in scripts/README.md +- [ ] Tested locally +- [ ] Passes `./scripts/run/script_health.sh` +- [ ] Passes all validation checks (`./scripts/run/validate_all.sh`) +- [ ] Passes `shellcheck` (if available) + +Quick validation command: +```bash +# Run all checks +./scripts/run/script_health.sh && ./scripts/run/validate_all.sh +``` + +## Version History + +| Version | Date | Description | +| ------- | ---------- | ----------- | +| 01.00.00 | 2025-01-03 | Initial enterprise standards documentation | + +## Metadata + +- **Document:** scripts/ENTERPRISE.md +- **Repository:** https://github.com/mokoconsulting-tech/moko-cassiopeia +- **Version:** 01.00.00 +- **Status:** Active diff --git a/scripts/README.md b/scripts/README.md index 41a1edc..5323fb6 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -253,8 +253,46 @@ WARN: ✗ tabs (warnings/issues found - run with -v for details) [SUCCESS] SUCCESS: All required checks passed ``` +### `script_health.sh` +Validates that all scripts follow enterprise standards: +- Checks for copyright headers +- Validates SPDX license identifiers +- Ensures FILE INFORMATION sections are present +- Verifies error handling (set -euo pipefail) +- Checks executable permissions + +Usage: +```bash +./scripts/run/script_health.sh # Standard mode +./scripts/run/script_health.sh -v # Verbose mode (shows details) +``` + +Example output: +``` +=== Script Health Summary === + Total scripts checked: 21 + Missing copyright: 0 + Missing SPDX identifier: 0 + Missing FILE INFORMATION: 0 + Missing error handling: 0 + Not executable: 0 +[SUCCESS] SUCCESS: All scripts follow enterprise standards +``` + ## Best Practices +### Enterprise Standards + +For comprehensive enterprise-grade scripting standards, see +[ENTERPRISE.md](./ENTERPRISE.md). + +Key highlights: +- **Error Handling**: Fail fast with clear, actionable messages +- **Security**: Input validation, no hardcoded secrets +- **Logging**: Structured output with timestamps +- **Portability**: Cross-platform compatibility +- **Documentation**: Usage functions and inline comments + ### Writing New Scripts 1. **Use the library functions**: @@ -313,6 +351,51 @@ Scripts are automatically executed in GitHub Actions workflows: - `.github/workflows/ci.yml` - Continuous integration - `.github/workflows/repo_health.yml` - Repository health checks +## Enterprise Features + +The scripts in this repository follow enterprise-grade standards: + +### Dependency Checking + +Scripts validate required dependencies at startup using `check_dependencies`: + +```bash +check_dependencies python3 git sed +``` + +### Timestamp Logging + +All major operations include timestamps for audit trails: + +```bash +log_info "Start time: $(log_timestamp)" +``` + +### Usage Documentation + +All user-facing scripts include comprehensive help: + +```bash +./scripts/run/validate_all.sh --help +./scripts/fix/versions.sh --help +``` + +### Standardized Exit Codes + +- `0` - Success +- `1` - Fatal error +- `2` - Invalid arguments + +### Enhanced Error Messages + +Clear, actionable error messages with context: + +```bash +die "Required file not found: ${CONFIG_FILE}. Run setup first." +``` + +See [ENTERPRISE.md](./ENTERPRISE.md) for complete standards documentation. + ## Version History | Version | Date | Description | diff --git a/scripts/fix/versions.sh b/scripts/fix/versions.sh index 491bf3c..b4c42b3 100755 --- a/scripts/fix/versions.sh +++ b/scripts/fix/versions.sh @@ -54,8 +54,19 @@ Arguments: Examples: $0 3.5.0 $0 1.2.3 + +Exit codes: + 0 - Version updated successfully + 1 - Invalid version format or update failed + 2 - Invalid arguments + +Files updated: + - Joomla manifest XML ( tag) + - package.json (if present) + - README.md (VERSION: references, if present) + USAGE -exit 1 +exit 0 } validate_version() { @@ -69,12 +80,21 @@ fi # Main # ---------------------------------------------------------------------------- +# Parse arguments +if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then + usage +fi + [ $# -eq 1 ] || usage VERSION="$1" validate_version "${VERSION}" +# Check dependencies +check_dependencies python3 + log_info "Updating version to: ${VERSION}" +log_info "Start time: $(log_timestamp)" # Source Joomla manifest utilities . "${SCRIPT_DIR}/lib/joomla_manifest.sh" @@ -149,6 +169,7 @@ fi log_info "=========================================" log_info "Version update completed: ${VERSION}" +log_info "End time: $(log_timestamp)" log_info "Files updated:" log_info " - ${MANIFEST}" [ -f "package.json" ] && log_info " - package.json" diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index 18becd7..ea365f4 100755 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -71,6 +71,20 @@ log_error() { die() { log_error "$*" + if [ "${VERBOSE_ERRORS:-true}" = "true" ]; then + echo "" >&2 + echo "Stack trace (last 10 commands):" >&2 + if [ -n "${BASH_VERSION:-}" ]; then + history | tail -10 >&2 2>/dev/null || true + fi + echo "" >&2 + echo "Environment:" >&2 + echo " PWD: $(pwd)" >&2 + echo " USER: ${USER:-unknown}" >&2 + echo " SHELL: ${SHELL:-unknown}" >&2 + echo " CI: ${CI:-false}" >&2 + echo "" >&2 + fi exit 1 } @@ -136,3 +150,84 @@ PY fail_if_root() { [ "$(id -u)" -eq 0 ] && die "Script must not run as root" } + +# ---------------------------------------------------------------------------- +# Enterprise features +# ---------------------------------------------------------------------------- + +# Check for required dependencies at script start +check_dependencies() { + local missing=0 + local missing_cmds=() + + for cmd in "$@"; do + if ! command -v "$cmd" >/dev/null 2>&1; then + log_error "Required command not found: $cmd" + missing=$((missing + 1)) + missing_cmds+=("$cmd") + fi + done + + if [ "$missing" -gt 0 ]; then + echo "" >&2 + echo "Missing required dependencies:" >&2 + for cmd in "${missing_cmds[@]}"; do + echo " - $cmd" >&2 + done + echo "" >&2 + echo "Installation guides:" >&2 + for cmd in "${missing_cmds[@]}"; do + case "$cmd" in + python3) + echo " python3: apt-get install python3 (Debian/Ubuntu) or brew install python3 (macOS)" >&2 + ;; + git) + echo " git: apt-get install git (Debian/Ubuntu) or brew install git (macOS)" >&2 + ;; + php) + echo " php: apt-get install php-cli (Debian/Ubuntu) or brew install php (macOS)" >&2 + ;; + xmllint) + echo " xmllint: apt-get install libxml2-utils (Debian/Ubuntu) or brew install libxml2 (macOS)" >&2 + ;; + *) + echo " $cmd: Please install via your system package manager" >&2 + ;; + esac + done + echo "" >&2 + die "Missing $missing required command(s)" + fi +} + +# Timeout wrapper for long-running commands +run_with_timeout() { + local timeout="$1" + shift + if command -v timeout >/dev/null 2>&1; then + timeout "$timeout" "$@" + else + "$@" + fi +} + +# Add script execution timestamp +log_timestamp() { + if command -v date >/dev/null 2>&1; then + printf '%s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" + fi +} + +# Calculate and log execution duration +log_duration() { + local start="$1" + local end="$2" + local duration=$((end - start)) + if [ "$duration" -ge 60 ]; then + local minutes=$((duration / 60)) + local seconds=$((duration % 60)) + printf '%dm %ds\n' "$minutes" "$seconds" + else + printf '%ds\n' "$duration" + fi +} diff --git a/scripts/run/script_health.sh b/scripts/run/script_health.sh new file mode 100755 index 0000000..ed0f313 --- /dev/null +++ b/scripts/run/script_health.sh @@ -0,0 +1,198 @@ +#!/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.Validate +# INGROUP: Script.Health +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/run/script_health.sh +# VERSION: 01.00.00 +# BRIEF: Validate scripts follow enterprise standards +# NOTE: Checks for copyright headers, error handling, and documentation +# ============================================================================ + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +. "${SCRIPT_DIR}/lib/common.sh" +. "${SCRIPT_DIR}/lib/logging.sh" + +# ---------------------------------------------------------------------------- +# Usage +# ---------------------------------------------------------------------------- + +usage() { +cat <<-USAGE +Usage: $0 [OPTIONS] + +Validate that all scripts follow enterprise standards. + +Options: + -v, --verbose Show detailed output + -h, --help Show this help message + +Checks performed: + - Copyright headers present + - SPDX license identifier present + - FILE INFORMATION section present + - set -euo pipefail present + - Executable permissions set + +Examples: + $0 # Run all health checks + $0 -v # Verbose output + $0 --help # Show usage + +Exit codes: + 0 - All checks passed + 1 - One or more checks failed + 2 - Invalid arguments + +USAGE +exit 0 +} + +# ---------------------------------------------------------------------------- +# Configuration +# ---------------------------------------------------------------------------- + +VERBOSE="${1:-}" + +case "${VERBOSE}" in + -h|--help) + usage + ;; + -v|--verbose) + VERBOSE="true" + ;; + "") + VERBOSE="false" + ;; + *) + log_error "Invalid argument: ${VERBOSE}" + echo "" + usage + exit 2 + ;; +esac + +log_info "Running script health checks" +log_info "Start time: $(log_timestamp)" + +START_TIME=$(date +%s) + +# ---------------------------------------------------------------------------- +# Health checks +# ---------------------------------------------------------------------------- + +total_scripts=0 +missing_copyright=0 +missing_spdx=0 +missing_fileinfo=0 +missing_error_handling=0 +not_executable=0 + +check_script() { + local script="$1" + local errors=0 + + total_scripts=$((total_scripts + 1)) + + # Check for copyright + if ! grep -q "Copyright (C)" "$script"; then + missing_copyright=$((missing_copyright + 1)) + errors=$((errors + 1)) + [ "${VERBOSE}" = "true" ] && log_warn "Missing copyright: $script" + fi + + # Check for SPDX + if ! grep -q "SPDX-License-Identifier" "$script"; then + missing_spdx=$((missing_spdx + 1)) + errors=$((errors + 1)) + [ "${VERBOSE}" = "true" ] && log_warn "Missing SPDX: $script" + fi + + # Check for FILE INFORMATION + if ! grep -q "FILE INFORMATION" "$script"; then + missing_fileinfo=$((missing_fileinfo + 1)) + errors=$((errors + 1)) + [ "${VERBOSE}" = "true" ] && log_warn "Missing FILE INFORMATION: $script" + fi + + # Check for error handling (bash scripts only) + if [[ "$script" == *.sh ]]; then + if ! grep -q "set -e" "$script"; then + missing_error_handling=$((missing_error_handling + 1)) + errors=$((errors + 1)) + [ "${VERBOSE}" = "true" ] && log_warn "Missing error handling: $script" + fi + fi + + # Check executable permission + if [ ! -x "$script" ]; then + not_executable=$((not_executable + 1)) + errors=$((errors + 1)) + [ "${VERBOSE}" = "true" ] && log_warn "Not executable: $script" + fi + + return $errors +} + +# Find all shell scripts +log_info "Scanning scripts directory..." + +while IFS= read -r -d '' script; do + check_script "$script" || true +done < <(find "${SCRIPT_DIR}" -type f -name "*.sh" -print0) + +# ---------------------------------------------------------------------------- +# Summary +# ---------------------------------------------------------------------------- + +END_TIME=$(date +%s) + +log_separator +log_info "Script Health Summary" +log_separator +log_kv "Total scripts checked" "${total_scripts}" +log_kv "Missing copyright" "${missing_copyright}" +log_kv "Missing SPDX identifier" "${missing_spdx}" +log_kv "Missing FILE INFORMATION" "${missing_fileinfo}" +log_kv "Missing error handling" "${missing_error_handling}" +log_kv "Not executable" "${not_executable}" +log_separator +log_info "End time: $(log_timestamp)" +log_info "Duration: $(log_duration "$START_TIME" "$END_TIME")" + +total_issues=$((missing_copyright + missing_spdx + missing_fileinfo + missing_error_handling + not_executable)) + +if [ "$total_issues" -eq 0 ]; then + log_success "SUCCESS: All scripts follow enterprise standards" + exit 0 +else + log_error "FAILED: Found ${total_issues} standard violation(s)" + log_info "Run with -v flag for details on which scripts need updates" + exit 1 +fi diff --git a/scripts/run/smoke_test.sh b/scripts/run/smoke_test.sh index d481ee5..66bec88 100755 --- a/scripts/run/smoke_test.sh +++ b/scripts/run/smoke_test.sh @@ -35,11 +35,43 @@ set -euo pipefail +# ---------------------------------------------------------------------------- +# Usage +# ---------------------------------------------------------------------------- + +usage() { +cat <<-USAGE +Usage: $0 [OPTIONS] + +Run basic smoke tests to verify repository structure and manifest validity. + +Options: + -h, --help Show this help message + +Examples: + $0 # Run all smoke tests + $0 --help # Show usage information + +USAGE +exit 0 +} + +# Parse arguments +if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then + usage +fi + # Source common utilities SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" . "${SCRIPT_DIR}/lib/common.sh" +# Check dependencies +check_dependencies python3 + +START_TIME=$(date +%s) + log_info "Running smoke tests for Moko-Cassiopeia repository" +log_info "Start time: $(log_timestamp)" # ---------------------------------------------------------------------------- # Test: Repository structure @@ -118,16 +150,29 @@ log_info "Checking PHP syntax..." if command -v php >/dev/null 2>&1; then php_errors=0 +failed_files=() while IFS= read -r -d '' f; do -if ! php -l "$f" >/dev/null 2>&1; then +if ! php_output=$(php -l "$f" 2>&1); then log_error "PHP syntax error in: $f" +echo " Error details:" >&2 +echo "$php_output" | sed 's/^/ /' >&2 +echo "" >&2 php_errors=$((php_errors + 1)) +failed_files+=("$f") 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 +echo "Summary of PHP syntax errors:" >&2 +echo " Total errors: ${php_errors}" >&2 +echo " Failed files:" >&2 +for f in "${failed_files[@]}"; do +echo " - $f" >&2 +done +echo "" >&2 +echo "To fix: Run 'php -l ' on each failed file for detailed error messages." >&2 die "Found ${php_errors} PHP syntax errors" fi else @@ -143,4 +188,7 @@ log_info "Smoke tests completed successfully" log_info "Extension: ${NAME}" log_info "Version: ${VERSION}" log_info "Type: ${TYPE}" +log_info "End time: $(log_timestamp)" +END_TIME=$(date +%s) +log_info "Duration: $(log_duration "$START_TIME" "$END_TIME")" log_info "=========================================" diff --git a/scripts/run/validate_all.sh b/scripts/run/validate_all.sh index 515cd6f..7149176 100755 --- a/scripts/run/validate_all.sh +++ b/scripts/run/validate_all.sh @@ -39,16 +39,65 @@ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" . "${SCRIPT_DIR}/lib/common.sh" . "${SCRIPT_DIR}/lib/logging.sh" +# ---------------------------------------------------------------------------- +# Usage +# ---------------------------------------------------------------------------- + +usage() { +cat <<-USAGE +Usage: $0 [OPTIONS] + +Run all validation scripts and report results. + +Options: + -v, --verbose Show detailed output from validation scripts + -h, --help Show this help message + +Examples: + $0 # Run all validations in quiet mode + $0 -v # Run with verbose output + $0 --help # Show usage information + +Exit codes: + 0 - All required checks passed + 1 - One or more required checks failed + 2 - Invalid arguments + +USAGE +exit 0 +} + # ---------------------------------------------------------------------------- # Configuration # ---------------------------------------------------------------------------- VERBOSE="${1:-}" -if [ "${VERBOSE}" = "-v" ] || [ "${VERBOSE}" = "--verbose" ]; then - VERBOSE="true" -else - VERBOSE="false" -fi + +# Parse arguments +case "${VERBOSE}" in + -h|--help) + usage + ;; + -v|--verbose) + VERBOSE="true" + ;; + "") + VERBOSE="false" + ;; + *) + log_error "Invalid argument: ${VERBOSE}" + echo "" + usage + exit 2 + ;; +esac + +# Check dependencies +check_dependencies python3 + +START_TIME=$(date +%s) + +log_info "Start time: $(log_timestamp)" REQUIRED_CHECKS=( "manifest" @@ -90,22 +139,20 @@ for check in "${REQUIRED_CHECKS[@]}"; do fi log_step "Running: ${check}" - 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 + # Always capture output for better error reporting + output="" + if output=$("${script}" 2>&1); then + log_success "✓ ${check}" + required_passed=$((required_passed + 1)) + [ "${VERBOSE}" = "true" ] && echo "$output" else - 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 + log_error "✗ ${check} (FAILED)" + required_failed=$((required_failed + 1)) + # Show error output for required checks regardless of verbose flag + echo "" >&2 + echo "Error output:" >&2 + echo "$output" >&2 + echo "" >&2 fi done @@ -121,21 +168,27 @@ for check in "${OPTIONAL_CHECKS[@]}"; do fi log_step "Running: ${check}" - 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 + # Capture output for better error reporting + output="" + if output=$("${script}" 2>&1); then + log_success "✓ ${check}" + optional_passed=$((optional_passed + 1)) + [ "${VERBOSE}" = "true" ] && echo "$output" else - if "${script}" >/dev/null 2>&1; then - log_success "✓ ${check}" - optional_passed=$((optional_passed + 1)) + log_warn "✗ ${check} (warnings/issues found)" + optional_failed=$((optional_failed + 1)) + # Show brief error summary for optional checks, full output in verbose + if [ "${VERBOSE}" = "true" ]; then + echo "" >&2 + echo "Error output:" >&2 + echo "$output" >&2 + echo "" >&2 else - log_warn "✗ ${check} (warnings/issues found - run with -v for details)" - optional_failed=$((optional_failed + 1)) + # Show first few lines of error + echo "" >&2 + echo "Error summary (run with -v for full details):" >&2 + echo "$output" | head -5 >&2 + echo "" >&2 fi fi done @@ -156,6 +209,10 @@ log_kv "Optional checks with issues" "${optional_failed}" log_separator +log_info "End time: $(log_timestamp)" +END_TIME=$(date +%s) +log_info "Duration: $(log_duration "$START_TIME" "$END_TIME")" + if [ "${required_failed}" -gt 0 ]; then log_error "FAILED: ${required_failed} required check(s) failed" exit 1 diff --git a/scripts/validate/changelog.sh b/scripts/validate/changelog.sh index c81d06e..c7eae46 100755 --- a/scripts/validate/changelog.sh +++ b/scripts/validate/changelog.sh @@ -1,4 +1,38 @@ #!/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.Validate +# INGROUP: Documentation +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/changelog.sh +# VERSION: 01.00.00 +# BRIEF: Validates CHANGELOG.md structure and version entries +# NOTE: Ensures changelog compliance with Keep a Changelog standard +# ============================================================================ + set -euo pipefail json_escape() { diff --git a/scripts/validate/language_structure.sh b/scripts/validate/language_structure.sh index 03b0054..753e971 100755 --- a/scripts/validate/language_structure.sh +++ b/scripts/validate/language_structure.sh @@ -1,4 +1,38 @@ #!/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.Validate +# INGROUP: Joomla.Language +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/language_structure.sh +# VERSION: 01.00.00 +# BRIEF: Validates Joomla language directory structure and INI files +# NOTE: Ensures proper language file organization +# ============================================================================ + set -euo pipefail SRC_DIR="${SRC_DIR:-src}" diff --git a/scripts/validate/license_headers.sh b/scripts/validate/license_headers.sh index 47b235d..75cfdf1 100755 --- a/scripts/validate/license_headers.sh +++ b/scripts/validate/license_headers.sh @@ -1,4 +1,38 @@ #!/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.Validate +# INGROUP: Licensing +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/license_headers.sh +# VERSION: 01.00.00 +# BRIEF: Checks that source files contain SPDX license identifiers +# NOTE: Ensures licensing compliance across codebase +# ============================================================================ + set -euo pipefail SRC_DIR="${SRC_DIR:-src}" diff --git a/scripts/validate/manifest.sh b/scripts/validate/manifest.sh index 28ef1d4..1574adc 100755 --- a/scripts/validate/manifest.sh +++ b/scripts/validate/manifest.sh @@ -1,6 +1,43 @@ #!/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.Validate +# INGROUP: Joomla.Manifest +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/manifest.sh +# VERSION: 01.00.00 +# BRIEF: Validates Joomla manifest XML structure and required fields +# NOTE: Ensures extension manifest compliance +# ============================================================================ + set -euo pipefail +# Input validation +SRC_DIR="${SRC_DIR:-src}" + log() { printf '%s\n' "$*"; } fail() { @@ -8,10 +45,14 @@ fail() { exit 1 } -SRC_DIR="${SRC_DIR:-src}" - +# Validate SRC_DIR if [ ! -d "${SRC_DIR}" ]; then - fail "${SRC_DIR} directory missing" + fail "${SRC_DIR} directory missing. Set SRC_DIR environment variable or ensure 'src' directory exists." +fi + +# Validate required dependencies +if ! command -v python3 >/dev/null 2>&1; then + fail "python3 is required but not found. Please install Python 3." fi # Candidate discovery policy: prefer explicit known names, otherwise fall back to extension-root manifests. @@ -51,7 +92,23 @@ if [ "${#manifest_candidates[@]}" -eq 0 ]; then fi if [ "${#manifest_candidates[@]}" -eq 0 ]; then - fail "No Joomla manifest XML found under ${SRC_DIR}" + { + echo "ERROR: No Joomla manifest XML found under ${SRC_DIR}" >&2 + echo "" >&2 + echo "Expected manifest file patterns:" >&2 + echo " - Template: ${SRC_DIR}/templateDetails.xml" >&2 + echo " - Package: ${SRC_DIR}/**/pkg_*.xml" >&2 + echo " - Component: ${SRC_DIR}/**/com_*.xml" >&2 + echo " - Module: ${SRC_DIR}/**/mod_*.xml" >&2 + echo " - Plugin: ${SRC_DIR}/**/plg_*.xml" >&2 + echo "" >&2 + echo "Troubleshooting:" >&2 + echo " 1. Verify the source directory exists: ls -la ${SRC_DIR}" >&2 + echo " 2. Check for XML files: find ${SRC_DIR} -name '*.xml'" >&2 + echo " 3. Ensure manifest contains root element" >&2 + echo "" >&2 + } >&2 + fail "No manifest found" fi # De-duplicate while preserving order. @@ -74,11 +131,21 @@ manifest_candidates=("${unique_candidates[@]}") if [ "${#manifest_candidates[@]}" -gt 1 ]; then { log "ERROR: Multiple manifest candidates detected. Resolve to exactly one primary manifest." >&2 - log "Candidates:" >&2 + log "" >&2 + log "Found ${#manifest_candidates[@]} candidates:" >&2 for c in "${manifest_candidates[@]}"; do - log "- ${c}" >&2 + log " - ${c}" >&2 done - } + log "" >&2 + log "Resolution options:" >&2 + log " 1. Remove redundant manifest files" >&2 + log " 2. Move extra manifests outside ${SRC_DIR}" >&2 + log " 3. Rename non-primary manifests to not match patterns (templateDetails.xml, pkg_*.xml, etc.)" >&2 + log "" >&2 + log "For package extensions, only the top-level package manifest should be in ${SRC_DIR}." >&2 + log "Child extension manifests should be in subdirectories." >&2 + log "" >&2 + } >&2 exit 1 fi diff --git a/scripts/validate/no_secrets.sh b/scripts/validate/no_secrets.sh index 0b5479e..4b50045 100755 --- a/scripts/validate/no_secrets.sh +++ b/scripts/validate/no_secrets.sh @@ -1,4 +1,38 @@ #!/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.Validate +# INGROUP: Security +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/no_secrets.sh +# VERSION: 01.00.00 +# BRIEF: Scan for accidentally committed secrets and credentials +# NOTE: High-signal pattern detection to prevent credential exposure +# ============================================================================ + set -euo pipefail SRC_DIR="${SRC_DIR:-src}" diff --git a/scripts/validate/paths.sh b/scripts/validate/paths.sh index ef4a75c..1a7f0b5 100755 --- a/scripts/validate/paths.sh +++ b/scripts/validate/paths.sh @@ -1,25 +1,77 @@ #!/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.Validate +# INGROUP: Path.Normalization +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/paths.sh +# VERSION: 01.00.00 +# BRIEF: Detect Windows-style path separators (backslashes) +# NOTE: Ensures cross-platform path compatibility +# ============================================================================ + set -euo pipefail # Detect Windows-style path literals (backslashes) in repository files. # Uses git ls-files -z and searches file contents for a literal backslash. hits=() +hit_lines=() + while IFS= read -r -d '' f; do # Skip common binary files by mime-type if file --brief --mime-type "$f" | grep -qE '^(application|audio|image|video)/'; then continue fi - if grep -F $'\\' -- "$f" >/dev/null 2>&1; then + # Find lines with backslashes and collect details + if backslash_lines=$(grep -n -F $'\\' -- "$f" 2>/dev/null); then hits+=("$f") + hit_lines+=("$backslash_lines") fi done < <(git ls-files -z) if [ "${#hits[@]}" -gt 0 ]; then - echo "ERROR: windows_path_literal_detected" - for h in "${hits[@]}"; do - echo " - ${h}" + echo "ERROR: Windows-style path literals detected" >&2 + echo "" >&2 + echo "Found backslashes in ${#hits[@]} file(s):" >&2 + for i in "${!hits[@]}"; do + echo "" >&2 + echo " File: ${hits[$i]}" >&2 + echo " Lines with backslashes:" >&2 + echo "${hit_lines[$i]}" | head -5 | sed 's/^/ /' >&2 + if [ "$(echo "${hit_lines[$i]}" | wc -l)" -gt 5 ]; then + echo " ... and $(($(echo "${hit_lines[$i]}" | wc -l) - 5)) more" >&2 + fi done + echo "" >&2 + echo "To fix:" >&2 + echo " 1. Run: ./scripts/fix/paths.sh" >&2 + echo " 2. Or manually replace backslashes (\\) with forward slashes (/)" >&2 + echo " 3. Ensure paths use POSIX separators for cross-platform compatibility" >&2 + echo "" >&2 exit 2 fi diff --git a/scripts/validate/php_syntax.sh b/scripts/validate/php_syntax.sh index ca6e9ef..cbb6988 100755 --- a/scripts/validate/php_syntax.sh +++ b/scripts/validate/php_syntax.sh @@ -1,6 +1,42 @@ #!/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.Validate +# INGROUP: Code.Quality +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/php_syntax.sh +# VERSION: 01.00.00 +# BRIEF: Validates PHP syntax using php -l on all PHP files +# NOTE: Requires PHP CLI to be available +# ============================================================================ + set -euo pipefail +# Validation timeout (seconds) - prevents hanging on problematic files +TIMEOUT="${VALIDATION_TIMEOUT:-30}" SRC_DIR="${SRC_DIR:-src}" json_escape() { @@ -22,17 +58,53 @@ fi failed=0 checked=0 +failed_files=() +failed_errors=() while IFS= read -r -d '' f; do checked=$((checked+1)) - if ! php -l "$f" >/dev/null; then - failed=1 + + # Capture actual error output + error_output="" + + # Use timeout if available to prevent hangs + if command -v timeout >/dev/null 2>&1; then + if ! error_output=$(timeout "${TIMEOUT}" php -l "$f" 2>&1); then + failed=1 + failed_files+=("$f") + failed_errors+=("$error_output") + fi + else + if ! error_output=$(php -l "$f" 2>&1); then + failed=1 + failed_files+=("$f") + failed_errors+=("$error_output") + fi fi done < <(find "${SRC_DIR}" -type f -name '*.php' -print0) if [ "${failed}" -ne 0 ]; then - printf '{"status":"fail","error":"php_lint_failed","files_checked":%s} -' "${checked}" + echo "ERROR: PHP syntax validation failed" >&2 + echo "Files checked: ${checked}" >&2 + echo "Files with errors: ${#failed_files[@]}" >&2 + echo "" >&2 + echo "Failed files and errors:" >&2 + for i in "${!failed_files[@]}"; do + echo " File: ${failed_files[$i]}" >&2 + echo " Error: ${failed_errors[$i]}" >&2 + echo "" >&2 + done + echo "" >&2 + echo "To fix: Review and correct the syntax errors in the files listed above." >&2 + echo "Run 'php -l ' on individual files for detailed error messages." >&2 + { + printf '{"status":"fail","error":"php_lint_failed","files_checked":%s,"failed_count":%s,"failed_files":[' "${checked}" "${#failed_files[@]}" + for i in "${!failed_files[@]}"; do + printf '%s' "$(json_escape "${failed_files[$i]}")" + [ "$i" -lt $((${#failed_files[@]} - 1)) ] && printf ',' + done + printf ']}\n' + } exit 1 fi diff --git a/scripts/validate/tabs.sh b/scripts/validate/tabs.sh index 77f66c8..0798ba9 100755 --- a/scripts/validate/tabs.sh +++ b/scripts/validate/tabs.sh @@ -1,4 +1,38 @@ #!/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.Validate +# INGROUP: Code.Quality +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/tabs.sh +# VERSION: 01.00.00 +# BRIEF: Detect TAB characters in YAML files where they are not allowed +# NOTE: YAML specification forbids tab characters +# ============================================================================ + set -euo pipefail # Detect TAB characters in source files tracked by Git. Uses careful @@ -14,15 +48,39 @@ if [ -z "${files}" ]; then fi bad=0 +bad_files=() +bad_lines=() + while IFS= read -r f; do - if grep -n $'\t' -- "$f" >/dev/null 2>&1; then - echo "TAB found in $f" + # Find lines with tabs and store them + if tab_lines=$(grep -n $'\t' -- "$f" 2>/dev/null); then + echo "TAB found in $f" >&2 + echo " Lines with tabs:" >&2 + echo "$tab_lines" | head -5 | sed 's/^/ /' >&2 + if [ "$(echo "$tab_lines" | wc -l)" -gt 5 ]; then + echo " ... and $(($(echo "$tab_lines" | wc -l) - 5)) more" >&2 + fi + echo "" >&2 bad=1 + bad_files+=("$f") fi done <<< "${files}" if [ "${bad}" -ne 0 ]; then + echo "" >&2 echo "ERROR: Tabs found in repository files" >&2 + echo "" >&2 + echo "YAML specification forbids tab characters." >&2 + echo "Found tabs in ${#bad_files[@]} file(s):" >&2 + for f in "${bad_files[@]}"; do + echo " - $f" >&2 + done + echo "" >&2 + echo "To fix:" >&2 + echo " 1. Run: ./scripts/fix/tabs.sh" >&2 + echo " 2. Or manually replace tabs with spaces in your editor" >&2 + echo " 3. Configure your editor to use spaces (not tabs) for YAML files" >&2 + echo "" >&2 exit 2 fi diff --git a/scripts/validate/version_alignment.sh b/scripts/validate/version_alignment.sh index 25c17ed..34767dd 100755 --- a/scripts/validate/version_alignment.sh +++ b/scripts/validate/version_alignment.sh @@ -1,4 +1,38 @@ #!/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.Validate +# INGROUP: Version.Management +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/version_alignment.sh +# VERSION: 01.00.00 +# BRIEF: Checks that manifest version is documented in CHANGELOG.md +# NOTE: Ensures version consistency across repository +# ============================================================================ + set -euo pipefail # Validate that the package/manifest version is present in CHANGELOG.md diff --git a/scripts/validate/xml_wellformed.sh b/scripts/validate/xml_wellformed.sh index 8571e8c..fae5968 100755 --- a/scripts/validate/xml_wellformed.sh +++ b/scripts/validate/xml_wellformed.sh @@ -1,5 +1,38 @@ #!/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.Validate +# INGROUP: XML.Validation +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# PATH: /scripts/validate/xml_wellformed.sh +# VERSION: 01.00.00 +# BRIEF: Validates that all XML files are well-formed +# NOTE: Uses Python ElementTree for portable XML parsing +# ============================================================================ + set -euo pipefail SRC_DIR="${SRC_DIR:-src}"