mirror of
https://github.com/1technophile/OpenMQTTGateway.git
synced 2026-02-20 00:32:04 +01:00
* Refactor GitHub Actions workflows for build, documentation, and linting - Consolidated build logic into reusable workflows (`task-build.yml` and `task-docs.yml`) to reduce duplication across multiple workflows. - Introduced `environments.json` to centralize the list of PlatformIO build environments, improving maintainability and clarity. - Updated `build.yml` and `build_and_docs_to_dev.yml` to utilize the new reusable workflows and environment definitions. - Enhanced `release.yml` to streamline the release process and integrate documentation generation. - Created reusable linting workflow (`task-lint.yml`) to standardize code formatting checks across the repository. - Simplified manual documentation workflow by leveraging the new reusable documentation workflow. - Improved artifact management and retention policies across workflows. - Updated dependencies and versions in workflows to ensure compatibility and performance. * CI/CD pipeline agnostic of Workflow Engine and integrated on github actions - Implemented ci.sh for orchestrating the complete build pipeline. - Created ci_00_config.sh for centralized configuration of build scripts. - Created ci_build_firmware.sh for building firmware for specified PlatformIO environments. - Created ci_prepare_artifacts.sh for preparing firmware artifacts for upload or deployment. - Created ci_set_version.sh for updating version tags in firmware configuration files. - Created ci_build.sh to orchestrate the complete build pipeline. - Created ci_qa.sh for code linting and formatting checks using clang-format. - Created ci_site.sh for building and deploying VuePress documentation with version management. - Implemented checks for required tools and dependencies in the new scripts. - Updated common_wu.py, compressFirmware.py, gen_wu.py, generate_board_docs.py, and prepare_deploy.sh with descriptive comments. Refactor CI/CD scripts for improved modularity and clarity - Consolidated build steps in task-build.yml to utilize ci.sh for version tagging, building, and artifact preparation. - Updated task-lint.yml to use ci.sh for code formatting checks instead of ci_qa.sh. - Enhanced CI_SCRIPTS.md documentation to reflect changes in script usage, command structure, and output organization. - Improved internal scripts for better error handling and logging. - Streamlined the output structure for build artifacts and documentation.
410 lines
11 KiB
Bash
Executable File
410 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# CI/CD Quality Assurance (QA) - Code Linting and Formatting
|
|
# Checks and fixes code formatting using clang-format
|
|
# Usage: ./scripts/ci_qa.sh [OPTIONS]
|
|
|
|
set -euo pipefail
|
|
|
|
# Constants
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
|
|
# Load shared configuration
|
|
if [[ -f "${SCRIPT_DIR}/ci_00_config.sh" ]]; then
|
|
source "${SCRIPT_DIR}/ci_00_config.sh"
|
|
else
|
|
echo "ERROR: ci_00_config.sh not found" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Default values
|
|
CHECK_MODE=true
|
|
FIX_MODE=false
|
|
FORMAT_ONLY=false
|
|
SOURCE_DIR="main"
|
|
EXTENSIONS="h,ino,cpp"
|
|
CLANG_FORMAT_VERSION="9"
|
|
VERBOSE=false
|
|
|
|
# Function to check if clang-format is available
|
|
check_clang_format() {
|
|
local version="$1"
|
|
local cmd="clang-format-${version}"
|
|
|
|
if command -v "$cmd" >/dev/null 2>&1; then
|
|
echo "$cmd"
|
|
return 0
|
|
fi
|
|
|
|
# Try without version suffix
|
|
if command -v clang-format >/dev/null 2>&1; then
|
|
echo "clang-format"
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Function to find files to check
|
|
find_files() {
|
|
local source="$1"
|
|
local extensions="$2"
|
|
|
|
if [[ ! -d "${PROJECT_ROOT}/${source}" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
local find_patterns=()
|
|
IFS=',' read -ra exts <<< "$extensions"
|
|
for ext in "${exts[@]}"; do
|
|
find_patterns+=(-name "*.${ext}" -o)
|
|
done
|
|
# Remove last -o
|
|
unset 'find_patterns[-1]'
|
|
|
|
local files
|
|
files=$(find "${PROJECT_ROOT}/${source}" -type f \( "${find_patterns[@]}" \) 2>/dev/null || true)
|
|
|
|
if [[ -z "$files" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
echo "$files"
|
|
}
|
|
|
|
# Function to check formatting
|
|
check_formatting() {
|
|
local clang_format_cmd="$1"
|
|
local files="$2"
|
|
|
|
log_info "Checking code formatting..."
|
|
|
|
local failed_files=()
|
|
local checked_count=0
|
|
local has_issues=false
|
|
|
|
while IFS= read -r file; do
|
|
if [[ -z "$file" ]]; then
|
|
continue
|
|
fi
|
|
|
|
checked_count=$((checked_count + 1))
|
|
|
|
if [[ "$VERBOSE" == true ]]; then
|
|
log_info "Checking: $file"
|
|
fi
|
|
|
|
# Check if file needs formatting and capture diff
|
|
local diff_output
|
|
diff_output=$("$clang_format_cmd" --dry-run --Werror "$file" 2>&1)
|
|
local format_result=$?
|
|
|
|
if [[ $format_result -ne 0 ]]; then
|
|
failed_files+=("$file")
|
|
has_issues=true
|
|
|
|
# Show the actual formatting differences
|
|
echo ""
|
|
log_warn "⚠ Formatting issues in: $file"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Generate and show diff with colors
|
|
local actual_diff
|
|
actual_diff=$(diff -u "$file" <("$clang_format_cmd" "$file") 2>/dev/null || true)
|
|
|
|
if [[ -n "$actual_diff" ]]; then
|
|
echo "$actual_diff" | head -50 | while IFS= read -r line; do
|
|
if [[ "$line" =~ ^-[^-] ]]; then
|
|
echo -e "\033[31m$line\033[0m" # Red for removed lines
|
|
elif [[ "$line" =~ ^+[^+] ]]; then
|
|
echo -e "\033[32m$line\033[0m" # Green for added lines
|
|
elif [[ "$line" =~ ^@@ ]]; then
|
|
echo -e "\033[36m$line\033[0m" # Cyan for line numbers
|
|
else
|
|
echo "$line"
|
|
fi
|
|
done
|
|
else
|
|
echo "$diff_output"
|
|
fi
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
else
|
|
if [[ "$VERBOSE" == true ]]; then
|
|
log_info " ✓ OK"
|
|
fi
|
|
fi
|
|
done <<< "$files"
|
|
|
|
echo ""
|
|
log_info "Checked ${checked_count} files"
|
|
|
|
if [[ $has_issues == true ]]; then
|
|
echo ""
|
|
log_error "Found ${#failed_files[@]} files with formatting issues:"
|
|
for file in "${failed_files[@]}"; do
|
|
log_error " - $file"
|
|
done
|
|
echo ""
|
|
log_error "To fix these issues automatically, run:"
|
|
log_error " $0 --fix"
|
|
echo ""
|
|
return 1
|
|
fi
|
|
|
|
log_info "✓ All files are properly formatted"
|
|
return 0
|
|
}
|
|
|
|
# Function to fix formatting
|
|
fix_formatting() {
|
|
local clang_format_cmd="$1"
|
|
local files="$2"
|
|
|
|
log_info "Fixing code formatting..."
|
|
|
|
local fixed_count=0
|
|
local total_count=0
|
|
|
|
while IFS= read -r file; do
|
|
if [[ -z "$file" ]]; then
|
|
continue
|
|
fi
|
|
|
|
total_count=$((total_count + 1))
|
|
|
|
if [[ "$VERBOSE" == true ]]; then
|
|
log_info "Processing: $file"
|
|
fi
|
|
|
|
# Apply formatting in-place
|
|
if "$clang_format_cmd" -i "$file" 2>/dev/null; then
|
|
fixed_count=$((fixed_count + 1))
|
|
if [[ "$VERBOSE" == true ]]; then
|
|
log_info " ✓ Formatted"
|
|
fi
|
|
else
|
|
if [[ "$VERBOSE" == true ]]; then
|
|
log_warn " ✗ Failed to format"
|
|
fi
|
|
fi
|
|
done <<< "$files"
|
|
|
|
echo ""
|
|
log_info "Processed ${total_count} files"
|
|
log_info "✓ Formatting applied to ${fixed_count} files"
|
|
|
|
if [[ $fixed_count -gt 0 ]]; then
|
|
log_warn ""
|
|
log_warn "Files have been modified. Please review and commit the changes:"
|
|
log_warn " git diff"
|
|
log_warn " git add -u"
|
|
log_warn " git commit -m 'style: apply clang-format'"
|
|
fi
|
|
}
|
|
|
|
# Function to run all QA checks
|
|
run_all_checks() {
|
|
log_info "Running all quality assurance checks..."
|
|
|
|
local all_passed=true
|
|
|
|
# Format check
|
|
log_info "═══ Code Formatting ═══"
|
|
if ! run_format_check; then
|
|
all_passed=false
|
|
fi
|
|
echo ""
|
|
|
|
# Future: Add more checks here
|
|
# - cppcheck static analysis
|
|
# - code complexity metrics
|
|
# - TODO/FIXME detection
|
|
# - license header validation
|
|
|
|
if [[ "$all_passed" == false ]]; then
|
|
log_error "Some QA checks failed"
|
|
return 1
|
|
fi
|
|
|
|
log_info "✓ All QA checks passed"
|
|
return 0
|
|
}
|
|
|
|
# Function to run format check
|
|
run_format_check() {
|
|
log_info "Checking for clang-format version ${CLANG_FORMAT_VERSION}..."
|
|
|
|
local clang_format_cmd
|
|
clang_format_cmd=$(check_clang_format "$CLANG_FORMAT_VERSION")
|
|
|
|
if [[ $? -ne 0 ]] || [[ -z "$clang_format_cmd" ]]; then
|
|
log_error "clang-format not found"
|
|
log_error "Please install clang-format:"
|
|
log_error " Ubuntu/Debian: sudo apt-get install clang-format-${CLANG_FORMAT_VERSION}"
|
|
log_error " macOS: brew install clang-format"
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$clang_format_cmd" == "clang-format-${CLANG_FORMAT_VERSION}" ]]; then
|
|
log_info "✓ clang-format-${CLANG_FORMAT_VERSION} found"
|
|
else
|
|
local installed_version
|
|
installed_version=$(clang-format --version | grep -oP '\d+\.\d+' | head -1 || echo "unknown")
|
|
log_warn "clang-format-${CLANG_FORMAT_VERSION} not found, using clang-format (version ${installed_version})"
|
|
fi
|
|
|
|
log_info "Finding files in '${SOURCE_DIR}' with extensions: ${EXTENSIONS}"
|
|
|
|
local files
|
|
files=$(find_files "$SOURCE_DIR" "$EXTENSIONS")
|
|
|
|
if [[ $? -ne 0 ]] || [[ -z "$files" ]]; then
|
|
log_error "Source directory not found: ${PROJECT_ROOT}/${SOURCE_DIR}"
|
|
return 1
|
|
fi
|
|
|
|
local file_count
|
|
file_count=$(echo "$files" | wc -l)
|
|
log_info "Found ${file_count} files to check"
|
|
|
|
if [[ "$FIX_MODE" == true ]]; then
|
|
fix_formatting "$clang_format_cmd" "$files"
|
|
else
|
|
check_formatting "$clang_format_cmd" "$files"
|
|
fi
|
|
}
|
|
|
|
# Function to show usage
|
|
usage() {
|
|
cat << EOF
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Run quality assurance checks on OpenMQTTGateway code.
|
|
|
|
Options:
|
|
--check Check formatting only (CI mode) [default]
|
|
--fix Fix formatting issues automatically
|
|
--format Run only format checks
|
|
--all Run all QA checks [default]
|
|
--source DIR Source directory to check [default: main]
|
|
--extensions EXTS File extensions (comma-separated) [default: h,ino,cpp]
|
|
--clang-format-version V clang-format version [default: 9]
|
|
--verbose Enable verbose output
|
|
--help Show this help message
|
|
|
|
Examples:
|
|
# Check formatting (CI mode)
|
|
$0 --check
|
|
|
|
# Fix formatting issues
|
|
$0 --fix
|
|
|
|
# Check specific directory
|
|
$0 --check --source lib/LEDManager
|
|
|
|
# Check with custom extensions
|
|
$0 --check --extensions h,cpp
|
|
|
|
# Verbose output
|
|
$0 --check --verbose
|
|
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# Parse command line arguments
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--check)
|
|
CHECK_MODE=true
|
|
FIX_MODE=false
|
|
shift
|
|
;;
|
|
--fix)
|
|
FIX_MODE=true
|
|
CHECK_MODE=false
|
|
shift
|
|
;;
|
|
--format)
|
|
FORMAT_ONLY=true
|
|
shift
|
|
;;
|
|
--all)
|
|
FORMAT_ONLY=false
|
|
shift
|
|
;;
|
|
--source)
|
|
SOURCE_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--extensions)
|
|
EXTENSIONS="$2"
|
|
shift 2
|
|
;;
|
|
--clang-format-version)
|
|
CLANG_FORMAT_VERSION="$2"
|
|
shift 2
|
|
;;
|
|
--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
--help)
|
|
usage
|
|
;;
|
|
*)
|
|
log_error "Unknown option: $1"
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
local start_time
|
|
start_time=$(date +%s)
|
|
|
|
parse_args "$@"
|
|
|
|
log_info "Starting QA pipeline..."
|
|
|
|
if [[ "$FIX_MODE" == true ]]; then
|
|
log_info "Mode: FIX (will modify files)"
|
|
else
|
|
log_info "Mode: CHECK (read-only)"
|
|
fi
|
|
|
|
# Run checks
|
|
local result=0
|
|
if [[ "$FORMAT_ONLY" == true ]]; then
|
|
run_format_check || result=$?
|
|
else
|
|
run_all_checks || result=$?
|
|
fi
|
|
|
|
local end_time
|
|
end_time=$(date +%s)
|
|
local duration=$((end_time - start_time))
|
|
|
|
echo ""
|
|
echo "╔════════════════════════════════════════╗"
|
|
echo "║ QA Pipeline Summary ║"
|
|
echo "╚════════════════════════════════════════╝"
|
|
echo " Duration: ${duration}s"
|
|
|
|
if [[ $result -eq 0 ]]; then
|
|
echo " Status: SUCCESS ✓"
|
|
echo "╚════════════════════════════════════════╝"
|
|
return 0
|
|
else
|
|
echo " Status: FAILED ✗"
|
|
echo "╚════════════════════════════════════════╝"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Execute main function
|
|
main "$@"
|