[CI] Refactor GitHub Actions workflows for build, documentation, and linting (#2260)

* 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.
This commit is contained in:
Alessandro Staniscia authored and GitHub committed 2026-02-15 14:58:58 -06:00
1 parent fd433c220e
commit 134c03362c
27 files changed
+4151 -346

No files matched your search

+575
View File
@@ -0,0 +1,575 @@
# CI/CD Scripts Documentation
This documentation describes the CI/CD scripts used to build OpenMQTTGateway firmware and documentation. These scripts work in GitHub Actions, locally, and in any CI/CD environment.
- [Overview](#cicd-scripts-documentation)
- [Quick Reference](#quick-reference)
- [Script Hierarchy](#script-hierarchy)
- [Script Description](#script-description)
- [Output Structure](#output-structure)
- [Commands](#commands)
- [ci.sh - Main Entry Point](#commands)
- [ci.sh build - Build Firmware](#cish-build---build-firmware)
- [ci.sh site - Build Documentation](#cish-site---build-documentation)
- [ci.sh qa - Code Formatting Check](#cish-qa---code-formatting-check)
- [Internal Scripts](#internal-scripts)
- [ci_set_version.sh](#ci_set_versionsh)
- [ci_build_firmware.sh](#ci_build_firmwaresh)
- [ci_prepare_artifacts.sh](#ci_prepare_artifactssh)
- [ci_00_config.sh](#ci_00_configsh)
- [Python Helper Scripts](#python-helper-scripts)
- [generate_board_docs.py](#generate_board_docspy)
- [gen_wu.py](#gen_wupy)
- [Environment Variables](#environment-variables)
- [Exit Codes](#exit-codes)
- [Environment Detection](#environment-detection)
## Quick Reference
### Script Hierarchy
```
ci.sh (dispatcher)
├── build → ci_build.sh → ci_set_version.sh
│ → ci_build_firmware.sh
│ → ci_prepare_artifacts.sh
├── site → ci_site.sh → generate_board_docs.py
│ → gen_wu.py
├── qa → ci_qa.sh → clang-format
└── all → qa + build + site (sequential)
```
### Script Description
| Script | Purpose | Called By |
|--------|---------|-----------|
| `ci.sh` | Main command dispatcher | User/GitHub Actions |
| `ci_build.sh` | Build firmware orchestrator | ci.sh build |
| `ci_site.sh` | Documentation build orchestrator | ci.sh site |
| `ci_qa.sh` | Code formatting checker | ci.sh qa |
| `ci_set_version.sh` | Version injection in firmware | ci_build.sh |
| `ci_build_firmware.sh` | PlatformIO build execution | ci_build.sh |
| `ci_prepare_artifacts.sh` | Artifact packaging | ci_build.sh |
| `ci_00_config.sh` | Shared configuration and functions | All scripts |
### Output Structure
Build outputs are organized in the project root:
```
.pio/build/<environment>/ # PlatformIO build outputs
├── firmware.bin # Main firmware binary
├── bootloader.bin # ESP32 bootloader
└── partitions.bin # ESP32 partition table
generated/
├── artifacts/ # Packaged firmware artifacts
└── site/ # Built documentation (VuePress output)
scripts/
├── latest_version.json # Production version metadata
└── latest_version_dev.json # Development version metadata
```
## Commands
`ci.sh` is the main Entry Point. Command dispatcher that routes to specialized scripts.
**Usage:**
```bash
./scripts/ci.sh <command> [OPTIONS]
```
**Commands:**
- `build` - Build firmware for specified environment
- `site` or `docs` - Build documentation website
- `qa` or `lint` - Run code formatting checks
- `all` or `pipeline` - Run complete pipeline (qa + build + site)
**Examples:**
```bash
# Get Help
./scripts/ci.sh build --help
./scripts/ci.sh qa --help
./scripts/ci.sh site --help
# Build firmware
./scripts/ci.sh build esp32dev-ble --mode dev
./scripts/ci.sh build esp32dev-all-test --version v1.8.0 --deploy-ready
# Build documentation
./scripts/ci.sh site --mode prod
./scripts/ci.sh site --mode dev --url-prefix /dev/
# Check code formatting
./scripts/ci.sh qa --check
./scripts/ci.sh qa --fix
# Run complete pipeline
./scripts/ci.sh all esp32dev-ble --version v1.8.0
./scripts/ci.sh all esp32dev-ble --no-site
```
**Options for `all` command:**
- `--no-site` - Skip documentation build step
- All options from `build` command are passed through
---
### ci.sh build - Build Firmware
Orchestrates complete firmware build: version injection, compilation, artifact packaging.
**Usage:**
```bash
./scripts/ci.sh build <environment> [OPTIONS]
```
**Required Arguments:**
- `<environment>` - PlatformIO environment name (e.g., esp32dev-ble, nodemcuv2-rf)
**Options:**
- `--version <tag>` - Version string to inject (default: auto-generated from git)
- `--mode <dev|prod>` - Build mode (default: prod)
- `dev` - Enables development OTA, sets DEVELOPMENTOTA=true
- `prod` - Standard production build
- `--deploy-ready` - Package artifacts with deployment naming (env-firmware.bin)
- `--output <dir>` - Output directory for artifacts (default: generated/artifacts)
- `--skip-verification` - Skip build tools verification
- `--clean` - Clean previous build before starting
- `--verbose` - Enable verbose PlatformIO output
- `--list-envs` - List all available PlatformIO environments
- `--help` - Show help message
**Execution Flow:**
```
ci.sh build esp32dev-ble --version v1.8.0 --mode prod --deploy-ready
│
├─> ci_build.sh (orchestrator)
│ ├─> verify_build_tools() - Check python3, platformio, git
│ ├─> ci_set_version.sh v1.8.0 - Inject version in User_config.h
│ ├─> ci_build_firmware.sh esp32dev-ble - Execute PlatformIO build
│ └─> ci_prepare_artifacts.sh esp32dev-ble --deploy - Package binaries
│
└─> Outputs in generated/artifacts/
├─ esp32dev-ble-firmware.bin
├─ esp32dev-ble-bootloader.bin
└─ esp32dev-ble-partitions.bin
```
**Examples:**
```bash
# Development build
./scripts/ci.sh build esp32dev-ble --mode dev
# Production build with version
./scripts/ci.sh build esp32dev-ble --version v1.8.0 --mode prod
# Deploy-ready build
./scripts/ci.sh build esp32dev-all-test --version v1.8.0 --deploy-ready
# Clean build with verbose output
./scripts/ci.sh build nodemcuv2-rf --clean --verbose
# List available environments
./scripts/ci.sh build --list-envs
```
**Environment Variables:**
- `CI` - Set to 'true' in CI/CD environments
- `BUILD_NUMBER` - Build number from CI/CD system
- `GIT_COMMIT` - Git commit hash for auto-versioning
- `PLATFORMIO_BUILD_FLAGS` - Additional PlatformIO flags (set by script when --mode dev)
**Output Files:**
- Standard mode: `firmware.bin`, `partitions.bin` in generated/artifacts/
- Deploy mode: `<env>-firmware.bin`, `<env>-bootloader.bin`, `<env>-partitions.bin`
---
### ci.sh site - Build Documentation
Builds VuePress documentation website with version management and WebUploader manifest generation.
**Usage:**
```bash
./scripts/ci.sh site [OPTIONS]
```
**Options:**
- `--mode <dev|prod>` - Documentation mode (default: prod)
- `dev` - Development documentation with watermark
- `prod` - Production documentation
- `--version-source <release|custom>` - Version source (default: release)
- `release` - Use git tag as version
- `custom` - Use custom version string
- `--custom-version <version>` - Custom version string (requires --version-source custom)
- `--url-prefix <path>` - Base URL path (default: /)
- Example: `/dev/` for development subdirectory
- `--no-webuploader` - Skip WebUploader manifest generation
- `--webuploader-args <args>` - Additional arguments for gen_wu.py
- `--preview` - Open browser after build (local development)
- `--help` - Show help message
**Execution Flow:**
```
ci.sh site --mode prod --version-source release
│
├─> ci_site.sh (orchestrator)
│ ├─> check_requirements() - Verify node, npm, python3, pip3
│ ├─> install_dependencies() - npm install, pip3 install packages
│ ├─> download_common_config() - Fetch from theengs.io
│ ├─> get_version() - Extract from git tag or use custom
│ ├─> set_version() - Update VuePress config and JSON files
│ ├─> set_url_prefix() - Set base path in config
│ ├─> generate_board_docs.py - Auto-generate board documentation
│ ├─> npm run docs:build - Build VuePress site
│ └─> gen_wu.py - Generate WebUploader manifest
│
└─> Outputs in generated/site/
├─ index.html
├─ assets/
└─ [board documentation pages]
```
**Examples:**
```bash
# Production documentation
./scripts/ci.sh site --mode prod
# Development documentation with custom version
./scripts/ci.sh site --mode dev --version-source custom --custom-version "DEVELOPMENT SHA:abc123"
# Documentation for /dev/ subdirectory
./scripts/ci.sh site --mode dev --url-prefix /dev/
# Skip WebUploader manifest
./scripts/ci.sh site --no-webuploader
# Local preview
./scripts/ci.sh site --preview
```
**Required Tools:**
- Node.js (for VuePress)
- npm (for package management)
- Python 3 (for board docs generator)
- pip3 (for Python dependencies: requests, pandas, markdown, pytablereader, tabulate)
**Output Files:**
- `generated/site/` - Complete static website
- `scripts/latest_version.json` - Production version metadata (updated)
- `scripts/latest_version_dev.json` - Development version metadata (updated)
---
### ci.sh qa - Code Formatting Check
Checks and fixes code formatting using clang-format.
**Usage:**
```bash
./scripts/ci.sh qa [OPTIONS]
```
**Options:**
- `--check` - Check formatting without modifying files (default)
- `--fix` - Automatically fix formatting issues
- `--source <dir>` - Source directory to check (default: main)
- `--extensions <list>` - File extensions to check, comma-separated (default: h,ino,cpp)
- `--clang-format-version <ver>` - clang-format version to use (default: 9)
- `--verbose` - Show detailed output for each file
- `--help` - Show help message
**Execution Flow:**
```
ci.sh qa --check --source main --extensions h,ino
│
├─> ci_qa.sh (orchestrator)
│ ├─> check_clang_format() - Find clang-format-9 or clang-format
│ ├─> find_files() - Locate files matching extensions in source dir
│ └─> check_formatting() - Run clang-format --dry-run --Werror
│ └─> Report files with formatting issues
│
└─> Exit code: 0 (pass) or 1 (formatting issues found)
```
**Examples:**
```bash
# Check formatting (CI mode)
./scripts/ci.sh qa --check
# Fix formatting automatically
./scripts/ci.sh qa --fix
# Check specific directory
./scripts/ci.sh qa --check --source lib
# Check only .h and .ino files
./scripts/ci.sh qa --check --extensions h,ino
# Check with verbose output
./scripts/ci.sh qa --check --verbose
# Use different clang-format version
./scripts/ci.sh qa --check --clang-format-version 11
```
**Required Tools:**
- clang-format (version specified, default: 9)
- Install: `sudo apt-get install clang-format-9`
**Output:**
- Check mode: Lists files with formatting issues and shows diffs
- Fix mode: Modifies files in-place and reports changes
- Exit code 0: All files properly formatted
- Exit code 1: Formatting issues found (in check mode)
---
## Internal Scripts
These scripts are called by the main orchestrators. It can be invoked directly but is not raccomanded.
### ci_set_version.sh
Injects version string into firmware configuration files.
**Called By:** `ci_build.sh`
**Usage:**
```bash
./scripts/ci_set_version.sh <version> [--dev]
```
**Arguments:**
- `<version>` - Version string to inject (e.g., v1.8.0 or abc123)
- `--dev` - Development mode (updates latest_version_dev.json)
**Files Modified:**
- `main/User_config.h` - Replaces "version_tag" with actual version
- `scripts/latest_version.json` - Production version metadata
- `scripts/latest_version_dev.json` - Development version metadata (--dev mode)
**Behavior:**
- Creates .bak backup files before modification
- Replaces all occurrences of "version_tag" string
- Validates version string (must not be empty or "version_tag")
- Cleans up backup files on success
---
### ci_build_firmware.sh
Executes PlatformIO build for specified environment.
**Called By:** `ci_build.sh`
**Usage:**
```bash
./scripts/ci_build_firmware.sh <environment> [OPTIONS]
```
**Arguments:**
- `<environment>` - PlatformIO environment name
**Options:**
- `--dev-ota` - Enable development OTA (sets PLATFORMIO_BUILD_FLAGS)
- `--clean` - Clean before build
- `--verbose` - Verbose PlatformIO output
**Environment Variables Set:**
- `PYTHONIOENCODING=utf-8`
- `PYTHONUTF8=1`
- `PLATFORMIO_BUILD_FLAGS="-DDEVELOPMENTOTA=true"` (when --dev-ota)
**PlatformIO Command:**
```bash
platformio run -e <environment> [--verbose]
```
**Output Location:**
- `.pio/build/<environment>/firmware.bin`
- `.pio/build/<environment>/bootloader.bin` (ESP32 only)
- `.pio/build/<environment>/partitions.bin` (ESP32 only)
---
### ci_prepare_artifacts.sh
Packages firmware binaries from PlatformIO build directory.
**Called By:** `ci_build.sh`
**Usage:**
```bash
./scripts/ci_prepare_artifacts.sh <environment> [OPTIONS]
```
**Arguments:**
- `<environment>` - PlatformIO environment name
**Options:**
- `--deploy` - Use deployment naming (prefix with environment name)
- `--output <dir>` - Output directory (default: generated/artifacts)
**Behavior:**
Standard mode (no --deploy):
- Copies: `firmware.bin`, `partitions.bin`
- Does NOT copy: `bootloader.bin`
Deploy mode (with --deploy):
- Copies and renames:
- `firmware.bin` → `<env>-firmware.bin`
- `bootloader.bin` → `<env>-bootloader.bin`
- `partitions.bin` → `<env>-partitions.bin`
**Source Location:**
- `.pio/build/<environment>/`
**Output Location:**
- Specified by `--output` or default `generated/artifacts/`
---
### ci_00_config.sh
Shared configuration and helper functions for all CI scripts.
**Sourced By:** All ci_*.sh scripts
**Provides:**
- Color codes for terminal output (BLUE, GREEN, RED, YELLOW, NC)
- Logging functions: `log_info()`, `log_warn()`, `log_error()`, `log_success()`
- Path constants: `BUILD_DIR`, `ARTIFACTS_DIR`, `SITE_DIR`
- Common utility functions
**Constants Defined:**
- `BUILD_DIR=".pio/build"` - PlatformIO build directory
- `ARTIFACTS_DIR="generated/artifacts"` - Artifact output directory
- `SITE_DIR="generated/site"` - Documentation output directory
**Logging Functions:**
```bash
log_info "message" # Blue [INFO] prefix
log_warn "message" # Yellow [WARN] prefix
log_error "message" # Red [ERROR] prefix
log_success "message" # Green [SUCCESS] prefix
```
---
## Python Helper Scripts
Other scripts are present and used as internal scripts and it's used as retrocompatibility. Below the lists:
- `generate_board_docs.py`
- `gen_wu.py`
### generate_board_docs.py
Auto-generates board-specific documentation pages from platformio.ini.
**Called By:** `ci_site.sh`
**Usage:**
```bash
python3 ./scripts/generate_board_docs.py
```
**Input:**
- `platformio.ini` - Board configurations
- `environments.ini` - Additional environments
**Output:**
- Markdown files in `docs/` directory for each board configuration
**Purpose:**
- Creates documentation pages for each hardware board
- Extracts configuration details from PlatformIO environment definitions
- Formats technical specifications and pin mappings
---
### gen_wu.py
Generates WebUploader manifest for OTA firmware updates.
**Called By:** `ci_site.sh`
**Usage:**
```bash
python3 ./scripts/gen_wu.py [--dev] [repository]
```
**Arguments:**
- `--dev` - Generate development manifest
- `repository` - GitHub repository name (e.g., 1technophile/OpenMQTTGateway)
**Input:**
- `.pio/build/<env>/firmware.bin` - Compiled firmware files
- `scripts/latest_version.json` or `scripts/latest_version_dev.json`
**Output:**
- WebUploader manifest JSON file in `docs/.vuepress/public/`
**Purpose:**
- Creates manifest for web-based firmware updater
- Lists available firmware files with metadata
- Used by documentation site for OTA updates
---
## Environment Variables
Scripts respect these environment variables:
- `PYTHONIOENCODING=utf-8`: Python encoding
- `PYTHONUTF8=1`: UTF-8 mode
- `PLATFORMIO_BUILD_FLAGS`: Custom build flags
- `ESP32_PLATFORM_VERSION`: Extracted automatically
---
## Exit Codes
All scripts use standard exit codes:
- `0` - Success
- `1` - General error or failure
- `2` - Missing required tools or dependencies
Scripts use `set -euo pipefail` for strict error handling:
- `-e` - Exit on error
- `-u` - Exit on undefined variable
- `-o pipefail` - Exit on pipe failure
---
## Environment Detection
Scripts automatically detect if running in CI/CD:
```bash
if [[ "${CI:-false}" == "true" ]]; then
# Running in CI/CD
# Disable interactive prompts
# Use different output formatting
fi
```
CI/CD environments typically set:
- `CI=true`
- `GITHUB_ACTIONS=true` (GitHub Actions)
- `BUILD_NUMBER` (build number)
- `GIT_COMMIT` (commit hash)
---
This documentation reflects the current implementation of CI/CD scripts. All scripts are located in `./scripts/` directory.
For GitHub Actions workflow documentation, see `.github/workflows/README.md`.
+2
View File
@@ -1,3 +1,5 @@
# Adds compiler flags to suppress warnings during PlatformIO build
# Used by: PlatformIO environments (esp32dev-pilight*, esp32-m5stick-c*)
Import("env")
Executable
+174
View File
@@ -0,0 +1,174 @@
#!/bin/bash
# CI/CD Main Entry Point - Command Dispatcher
# Routes commands to specialized scripts for build, site, qa, and all
# Usage: ./scripts/ci.sh <command> [OPTIONS]
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Load shared configuration (colors, logging functions, paths)
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
# Function to print banner
print_banner() {
echo "╔════════════════════════════════════════╗"
echo "║ OpenMQTTGateway CI/CD Pipeline ║"
echo "╚════════════════════════════════════════╝"
echo ""
}
# Show usage
usage() {
cat << EOF
Usage: $0 <command> [OPTIONS]
OpenMQTTGateway CI/CD Pipeline - Main Entry Point
Commands:
build Build firmware for specified environment
site Build and deploy documentation/website
qa Run quality assurance checks (linting, formatting)
all Run complete pipeline (qa + build + site)
Examples:
# Build firmware
$0 build esp32dev-all-test --mode dev
$0 build esp32dev-bt --version v1.8.0 --deploy-ready
# Build and deploy documentation
$0 site --mode prod --deploy
$0 site --mode dev --preview
# Run quality checks
$0 qa --check
$0 qa --fix
# Run complete pipeline
$0 all esp32dev-bt --version v1.8.0
Get help for specific commands:
$0 build --help
$0 site --help
$0 qa --help
EOF
exit 0
}
# Function to run build pipeline
run_build_pipeline() {
log_info "Executing build pipeline..."
"${SCRIPT_DIR}/ci_build.sh" "$@"
}
# Function to run site pipeline
run_site_pipeline() {
log_info "Executing site pipeline..."
"${SCRIPT_DIR}/ci_site.sh" "$@"
}
# Function to run QA pipeline
run_qa_pipeline() {
log_info "Executing QA pipeline..."
"${SCRIPT_DIR}/ci_qa.sh" "$@"
}
# Function to run complete pipeline
run_all_pipeline() {
local start_time
start_time=$(date +%s)
log_info "Starting complete CI/CD pipeline..."
echo ""
# Step 1: Quality Assurance
log_info "═══ Step 1/3: Quality Assurance ═══"
run_qa_pipeline --check || {
log_error "QA checks failed. Pipeline aborted."
return 1
}
echo ""
# Step 2: Build Firmware
log_info "═══ Step 2/3: Build Firmware ═══"
run_build_pipeline "$@" || {
log_error "Build failed. Pipeline aborted."
return 1
}
echo ""
# Step 3: Build Site (only if not in --no-site mode)
if [[ ! " $* " =~ " --no-site " ]]; then
log_info "═══ Step 3/3: Build Documentation ═══"
run_site_pipeline --mode prod || {
log_warn "Site build failed, but continuing..."
}
else
log_info "Skipping site build (--no-site flag)"
fi
local end_time
end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ""
echo "╔════════════════════════════════════════╗"
echo "║ Complete Pipeline Summary ║"
echo "╚════════════════════════════════════════╝"
echo " Total Duration: ${duration}s"
echo " Status: SUCCESS ✓"
echo "╚════════════════════════════════════════╝"
}
# Main execution
main() {
# Check if no arguments provided
if [[ $# -eq 0 ]]; then
print_banner
usage
fi
# Get command
local command="$1"
shift || true
# Handle help flags
if [[ "$command" == "--help" || "$command" == "-h" ]]; then
print_banner
usage
fi
print_banner
# Route to appropriate pipeline
case "$command" in
build)
run_build_pipeline "$@"
;;
site|docs)
run_site_pipeline "$@"
;;
qa|lint)
run_qa_pipeline "$@"
;;
all|pipeline)
run_all_pipeline "$@"
;;
*)
log_error "Unknown command: $command"
echo ""
usage
;;
esac
}
# Execute main function
main "$@"
+58
View File
@@ -0,0 +1,58 @@
# Build Scripts Configuration
# Used by: All build scripts for centralized configuration
# Python Configuration
PYTHON_VERSION="3.13"
PLATFORMIO_VERSION="https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.18.zip"
# Centralized Output Directory Structure
# All CI/CD generated files go under generated/
GENERATED_BASE_DIR="generated"
ARTIFACTS_DIR="${GENERATED_BASE_DIR}/artifacts"
SITE_DIR="${GENERATED_BASE_DIR}/site"
REPORTS_DIR="${GENERATED_BASE_DIR}/reports"
# PlatformIO Directory Configuration
BUILD_DIR=".pio/build"
SCRIPTS_DIR="scripts"
# Build Configuration
DEFAULT_ENVIRONMENT="esp32dev-all-test"
ENABLE_VERBOSE_BUILD="false"
ENABLE_BUILD_CACHE="true"
# Artifact Configuration
ARTIFACT_RETENTION_DAYS="7"
CREATE_MANIFEST="true"
COMPRESS_ARTIFACTS="false"
# Version Configuration
VERSION_FILE_PROD="scripts/latest_version.json"
VERSION_FILE_DEV="scripts/latest_version_dev.json"
USER_CONFIG_FILE="main/User_config.h"
# Logging Configuration
ENABLE_COLOR_OUTPUT="true"
LOG_LEVEL="INFO" # DEBUG, INFO, WARN, ERROR
# ============================================================================
# Colors - ANSI color codes for terminal output
# ============================================================================
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color
# ============================================================================
# Logging Functions - Standardized logging across all build scripts
# ============================================================================
log_info() { echo -e "${GREEN}[INFO]${NC} $*" >&2; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; }
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
log_step() { echo -e "${BLUE}[STEP]${NC} $*" >&2; }
# Advanced Options
ENABLE_CCACHE="false"
CCACHE_DIR=".ccache"
MAX_BUILD_JOBS="4"
+384
View File
@@ -0,0 +1,384 @@
#!/bin/bash
# CI/CD agnostic wrapper for complete build pipeline
# Orchestrates all build scripts with a single command
# Usage: ./scripts/ci.sh <environment> [OPTIONS]
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Load shared configuration (colors, logging functions, paths)
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
# Function to print banner
print_banner() {
echo "╔════════════════════════════════════════╗"
echo "║ OpenMQTTGateway CI/CD Build ║"
echo "╚════════════════════════════════════════╝"
echo ""
}
# Function to print summary
print_summary() {
local env="$1"
local version="$2"
local start_time="$3"
local end_time
end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ""
echo "╔════════════════════════════════════════╗"
echo "║ Build Summary ║"
echo "╚════════════════════════════════════════╝"
echo " Environment: $env"
echo " Version: $version"
echo " Duration: ${duration}s"
echo " Status: SUCCESS ✓"
echo "╚════════════════════════════════════════╝"
}
# Function to check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to get command version
get_command_version() {
local cmd="$1"
case "$cmd" in
platformio)
platformio --version 2>&1 | head -n1 | grep -oP '\d+\.\d+\.\d+' || echo "unknown"
;;
python|python3)
python3 --version 2>&1 | grep -oP '\d+\.\d+' || echo "unknown"
;;
*)
echo "unknown"
;;
esac
}
# Function to verify required tools
verify_build_tools() {
log_info "Verifying required build tools..."
local missing_tools=()
local version_mismatch=()
# Check Python
if ! command_exists python3; then
missing_tools+=("python3")
else
local python_version
python_version=$(get_command_version python3)
log_info "✓ Python ${python_version} found"
fi
# Check PlatformIO
if ! command_exists platformio; then
missing_tools+=("platformio")
else
local pio_version
pio_version=$(get_command_version platformio)
log_info "✓ PlatformIO ${pio_version} found"
fi
# Check git (for version auto-generation)
if ! command_exists git; then
log_warn "git not found (optional, but recommended)"
else
log_info "✓ git found"
fi
# Report missing tools
if [[ ${#missing_tools[@]} -gt 0 ]]; then
log_error "Missing required tools: ${missing_tools[*]}"
log_error ""
log_error "Please install missing tools:"
for tool in "${missing_tools[@]}"; do
case "$tool" in
python3)
log_error " - Python 3: https://www.python.org/downloads/"
;;
platformio)
log_error " - PlatformIO: pip3 install platformio"
log_error " or: pip3 install ${PLATFORMIO_VERSION:-platformio}"
;;
esac
done
log_error ""
log_error "Or skip this check with: --skip-verification"
return 1
fi
log_info "All required tools are available"
return 0
}
# Function to cleanup on error
cleanup_on_error() {
log_error "Build failed, cleaning up..."
# Restore any backups
find . -name "*.bak" -type f -exec bash -c 'mv "$1" "${1%.bak}"' _ {} \; 2>/dev/null || true
}
# Function to list available environments
list_environments() {
log_info "Available PlatformIO environments:"
echo ""
local env_files=("${PROJECT_ROOT}/platformio.ini" "${PROJECT_ROOT}/environments.ini")
local envs=()
for file in "${env_files[@]}"; do
if [[ -f "$file" ]]; then
while IFS= read -r line; do
if [[ "$line" =~ ^\[env:([^\]]+)\] ]]; then
local env_name="${BASH_REMATCH[1]}"
# Skip test environments
if [[ ! "$env_name" =~ -test$ && ! "$env_name" =~ -all- ]]; then
envs+=("$env_name")
fi
fi
done < "$file"
fi
done
# Sort and display unique environments
if [[ ${#envs[@]} -gt 0 ]]; then
printf '%s\n' "${envs[@]}" | sort -u | column -c 80
else
log_warn "No environments found in configuration files"
fi
echo ""
log_info "Total: $(printf '%s\n' "${envs[@]}" | sort -u | wc -l) environments"
}
# Show usage
usage() {
cat << EOF
Usage: $0 <environment> [OPTIONS]
Complete CI/CD build pipeline wrapper.
Arguments:
environment PlatformIO environment name
Options:
--version [TAG] Set version tag (if TAG omitted, auto-generated)
--mode MODE Build mode: 'prod' or 'dev' [default: prod]
'dev' enables OTA and development version
--deploy-ready Prepare for deployment (renamed artifacts)
--output DIR Output directory for artifacts [default: generated/artifacts/]
--skip-verification Skip build tools verification
--clean Clean build before starting
--verbose Enable verbose output
--list-envs List all available PlatformIO environments
--help Show this help message
Environment Variables:
CI Set to 'true' in CI/CD environments
BUILD_NUMBER Build number from CI/CD
GIT_COMMIT Git commit hash for versioning
Examples:
# List available environments
$0 --list-envs
# Local development build
$0 esp32dev-all-test --mode dev
# Production release build
$0 esp32dev-bt --version v1.7.0 --mode prod --deploy-ready
# CI/CD build (auto-detects version)
$0 theengs-bridge --version --mode dev
EOF
}
# Main pipeline
main() {
local environment=""
local version=""
local set_version=false
local mode=""
local deploy=false
local output_dir=""
local skip_verification=false
local clean=false
local verbose=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--version)
set_version=true
# Check if next argument is a version tag or another option
if [[ $# -gt 1 && ! "$2" =~ ^-- ]]; then
version="$2"
shift 2
else
shift
fi
;;
--mode)
if [[ $# -lt 2 ]]; then
log_error "--mode requires an argument: 'prod' or 'dev'"
usage
exit 1
fi
if [[ "$2" != "prod" && "$2" != "dev" ]]; then
log_error "Invalid mode: $2. Must be 'prod' or 'dev'"
usage
exit 1
fi
mode="$2"
shift 2
;;
--deploy-ready)
deploy=true
shift
;;
--output)
if [[ $# -lt 2 ]]; then
log_error "--output requires a directory argument"
usage
exit 1
fi
output_dir="$2"
shift 2
;;
--skip-verification)
skip_verification=true
shift
;;
--clean)
clean=true
shift
;;
--verbose)
verbose=true
shift
;;
--list-envs)
list_environments
exit 0
;;
--help|-h)
usage
exit 0
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
environment="$1"
shift
;;
esac
done
# Set default mode if not specified
if [[ -z "$mode" ]]; then
mode="prod"
log_info "Mode not specified, defaulting to production"
fi
# Validate environment
if [[ -z "$environment" ]]; then
log_error "Environment name is required"
usage
exit 1
fi
# Auto-generate version if --version flag is set but no tag provided
if [[ "$set_version" == "true" && -z "$version" ]]; then
if [[ "${CI:-false}" == "true" ]]; then
# CI/CD environment
version="${BUILD_NUMBER:-${GIT_COMMIT:-unknown}}"
else
# Local development
version="local-$(date +%Y%m%d-%H%M%S)"
fi
log_info "Auto-generated version: $version"
fi
# Setup error handling
trap cleanup_on_error ERR
# Change to project root
cd "$PROJECT_ROOT"
# Start timer
local start_time
start_time=$(date +%s)
# Print banner
print_banner
# Step 1: Verify build tools
if [[ "$skip_verification" == "false" ]]; then
log_step "1/4 Verifying build tools..."
verify_build_tools || exit 1
echo ""
else
log_warn "Skipping build tools verification (--skip-verification)"
echo ""
fi
# Step 2: Set version (only if --version flag was provided)
if [[ "$set_version" == "true" ]]; then
log_step "2/4 Setting version: $version"
if [[ "$mode" == "dev" ]]; then
"${SCRIPT_DIR}/ci_set_version.sh" "$version" --dev || exit 1
else
"${SCRIPT_DIR}/ci_set_version.sh" "$version" --prod || exit 1
fi
echo ""
else
log_info "Skipping version setting (use --version to set version)"
echo ""
fi
# Step 3: Build firmware
log_step "3/4 Building firmware for: $environment"
local build_opts=()
[[ "$mode" == "dev" ]] && build_opts+=(--dev-ota)
[[ "$clean" == "true" ]] && build_opts+=(--clean)
[[ "$verbose" == "true" ]] && build_opts+=(--verbose)
"${SCRIPT_DIR}/ci_build_firmware.sh" "$environment" "${build_opts[@]}" || exit 1
echo ""
# Step 4: Prepare artifacts
log_step "4/4 Preparing artifacts..."
local artifact_opts=()
[[ "$deploy" == "true" ]] && artifact_opts+=(--deploy) || artifact_opts+=(--standard)
artifact_opts+=(--manifest)
[[ -n "$output_dir" ]] && artifact_opts+=(--output "$output_dir")
"${SCRIPT_DIR}/ci_prepare_artifacts.sh" "$environment" "${artifact_opts[@]}" || exit 1
echo ""
# Print summary
print_summary "$environment" "$version" "$start_time"
log_info "✓ Complete build pipeline finished successfully"
}
# Run main if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
+281
View File
@@ -0,0 +1,281 @@
#!/bin/bash
# Builds firmware for specified PlatformIO environment
# Used by: CI/CD pipelines and local development
# Usage: ./build_firmware.sh <environment> [OPTIONS]
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Load shared configuration (colors, logging functions, paths)
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
# Set absolute path for BUILD_DIR
BUILD_DIR="${PROJECT_ROOT}/${BUILD_DIR}"
# Script-specific logging function
log_build() { echo -e "${BLUE}[BUILD]${NC} $*"; }
# Function to validate environment name
validate_environment() {
local env="$1"
if [[ -z "$env" ]]; then
log_error "Environment name is required"
return 1
fi
# Check if environment exists in platformio.ini or environments.ini
if ! grep -q "^\[env:${env}\]" "${PROJECT_ROOT}/platformio.ini" "${PROJECT_ROOT}/environments.ini" 2>/dev/null; then
log_warn "Environment '${env}' not found in configuration files"
log_warn "Proceeding anyway (PlatformIO will validate)"
fi
log_info "Building environment: $env"
}
# Function to setup build environment variables
setup_build_env() {
local enable_dev_ota="${1:-false}"
export PYTHONIOENCODING=utf-8
export PYTHONUTF8=1
if [[ "$enable_dev_ota" == "true" ]]; then
export PLATFORMIO_BUILD_FLAGS='"-DDEVELOPMENTOTA=true"'
log_info "Development OTA enabled"
fi
}
# Function to check PlatformIO availability
check_platformio() {
if ! command -v platformio >/dev/null 2>&1; then
log_error "PlatformIO not found. Run setup_build_env.sh first"
return 1
fi
}
# Function to clean build artifacts
clean_build() {
local env="$1"
local env_dir="${BUILD_DIR}/${env}"
if [[ -d "$env_dir" ]]; then
log_info "Cleaning previous build artifacts for: $env"
rm -rf "$env_dir"
fi
}
# Function to run PlatformIO build
run_build() {
local env="$1"
local clean="${2:-false}"
local verbose="${3:-false}"
log_build "Starting build for environment: $env"
local build_cmd="platformio run -e $env"
if [[ "$clean" == "true" ]]; then
build_cmd="platformio run -e $env --target clean && $build_cmd"
fi
if [[ "$verbose" == "true" ]]; then
build_cmd="$build_cmd --verbose"
fi
# Execute build with timing
local start_time
start_time=$(date +%s)
if eval "$build_cmd"; then
local end_time
end_time=$(date +%s)
local duration=$((end_time - start_time))
log_build "Build completed successfully in ${duration}s"
return 0
else
log_error "Build failed for environment: $env"
return 1
fi
}
# Function to verify build artifacts
verify_artifacts() {
local env="$1"
local env_dir="${BUILD_DIR}/${env}"
log_info "Verifying build artifacts..."
local artifacts_found=0
local firmware="${env_dir}/firmware.bin"
local partitions="${env_dir}/partitions.bin"
local bootloader="${env_dir}/bootloader.bin"
if [[ -f "$firmware" ]]; then
local size
size=$(stat -f%z "$firmware" 2>/dev/null || stat -c%s "$firmware" 2>/dev/null)
log_info "✓ firmware.bin (${size} bytes)"
((artifacts_found++))
else
log_warn "✗ firmware.bin not found"
fi
if [[ -f "$partitions" ]]; then
log_info "✓ partitions.bin"
((artifacts_found++))
fi
if [[ -f "$bootloader" ]]; then
log_info "✓ bootloader.bin"
((artifacts_found++))
fi
if [[ $artifacts_found -eq 0 ]]; then
log_error "No build artifacts found"
return 1
fi
log_info "Found ${artifacts_found} artifact(s)"
}
# Function to show build summary
show_build_summary() {
local env="$1"
local env_dir="${BUILD_DIR}/${env}"
echo ""
echo "═══════════════════════════════════════"
echo " Build Summary: $env"
echo "═══════════════════════════════════════"
if [[ -d "$env_dir" ]]; then
find "$env_dir" -name "*.bin" -o -name "*.elf" | while read -r file; do
local size
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
local size_kb=$((size / 1024))
echo " $(basename "$file"): ${size_kb} KB"
done
fi
echo "═══════════════════════════════════════"
}
# Show usage
usage() {
cat << EOF
Usage: $0 <environment> [OPTIONS]
Build firmware for a specific PlatformIO environment.
Arguments:
environment PlatformIO environment name (e.g., esp32dev-all-test)
Options:
--dev-ota Enable development OTA build flags
--clean Clean build artifacts before building
--verbose Enable verbose build output
--no-verify Skip artifact verification
--help Show this help message
Examples:
$0 esp32dev-all-test
$0 esp32dev-bt --dev-ota
$0 theengs-bridge --clean --verbose
EOF
}
# Main execution
main() {
local environment=""
local enable_dev_ota=false
local clean_build_flag=false
local verbose=false
local verify=true
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--dev-ota)
enable_dev_ota=true
shift
;;
--clean)
clean_build_flag=true
shift
;;
--verbose)
verbose=true
shift
;;
--no-verify)
verify=false
shift
;;
--help|-h)
usage
exit 0
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
environment="$1"
shift
;;
esac
done
# Validate inputs
if [[ -z "$environment" ]]; then
log_error "Environment name is required"
usage
exit 1
fi
# Change to project root
cd "$PROJECT_ROOT"
# Check prerequisites
check_platformio || exit 1
# Validate environment
validate_environment "$environment" || exit 1
# Setup build environment
setup_build_env "$enable_dev_ota"
# Clean if requested
if [[ "$clean_build_flag" == "true" ]]; then
clean_build "$environment"
fi
# Run build
run_build "$environment" "$clean_build_flag" "$verbose" || exit 1
# Verify artifacts
if [[ "$verify" == "true" ]]; then
verify_artifacts "$environment" || exit 1
fi
# Show summary
show_build_summary "$environment"
log_info "Build process completed successfully"
}
# Run main if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
+318
View File
@@ -0,0 +1,318 @@
#!/bin/bash
# Prepares firmware artifacts for upload or deployment
# Used by: CI/CD pipelines for artifact packaging
# Usage: ./prepare_artifacts.sh <environment> [OPTIONS]
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Load shared configuration (colors, logging functions, paths)
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
# Set absolute paths
BUILD_DIR="${PROJECT_ROOT}/${BUILD_DIR}"
DEFAULT_OUTPUT_DIR="${PROJECT_ROOT}/${ARTIFACTS_DIR}"
# Function to create output directory
create_output_dir() {
local output_dir="$1"
if [[ -d "$output_dir" ]]; then
log_warn "Output directory already exists: $output_dir"
log_info "Cleaning existing artifacts..."
rm -rf "$output_dir"
fi
mkdir -p "$output_dir"
log_info "Created output directory: $output_dir"
}
# Function to copy artifact with optional renaming
copy_artifact() {
local source="$1"
local dest="$2"
local artifact_type="$3"
if [[ ! -f "$source" ]]; then
log_warn "${artifact_type} not found: $source"
return 1
fi
if cp "$source" "$dest"; then
local size
size=$(stat -f%z "$dest" 2>/dev/null || stat -c%s "$dest" 2>/dev/null)
local size_kb=$((size / 1024))
log_info "✓ Copied ${artifact_type}: $(basename "$dest") (${size_kb} KB)"
return 0
else
log_error "Failed to copy ${artifact_type}: $source"
return 1
fi
}
# Function to prepare standard artifacts (no renaming)
prepare_standard_artifacts() {
local env="$1"
local output_dir="$2"
local env_dir="${BUILD_DIR}/${env}"
log_info "Preparing STANDARD artifacts for: $env"
local copied=0
# Copy firmware.bin (required)
if copy_artifact "${env_dir}/firmware.bin" "${output_dir}/firmware.bin" "firmware"; then
((copied++))
fi
# Copy partitions.bin (optional)
copy_artifact "${env_dir}/partitions.bin" "${output_dir}/partitions.bin" "partitions" && ((copied++)) || true
# Note: bootloader.bin is NOT copied in standard mode (only needed for deployment)
if [[ $copied -eq 0 ]]; then
log_error "No artifacts were copied"
return 1
fi
log_info "Copied ${copied} artifact(s) in standard mode"
}
# Function to prepare deployment artifacts (with renaming)
prepare_deployment_artifacts() {
local env="$1"
local output_dir="$2"
local env_dir="${BUILD_DIR}/${env}"
log_info "Preparing DEPLOYMENT artifacts for: $env"
local copied=0
# Copy and rename firmware.bin
if copy_artifact "${env_dir}/firmware.bin" "${output_dir}/${env}-firmware.bin" "firmware"; then
((copied++))
fi
# Copy and rename partitions.bin (optional)
copy_artifact "${env_dir}/partitions.bin" "${output_dir}/${env}-partitions.bin" "partitions" && ((copied++)) || true
# Copy and rename bootloader.bin (optional)
copy_artifact "${env_dir}/bootloader.bin" "${output_dir}/${env}-bootloader.bin" "bootloader" && ((copied++)) || true
# Copy boot_app0.bin if exists (ESP32 specific)
copy_artifact "${env_dir}/boot_app0.bin" "${output_dir}/${env}-boot_app0.bin" "boot_app0" && ((copied++)) || true
if [[ $copied -eq 0 ]]; then
log_error "No artifacts were copied"
return 1
fi
log_info "Copied ${copied} artifact(s) in deployment mode"
}
# Function to create manifest file
create_manifest() {
local env="$1"
local output_dir="$2"
local manifest="${output_dir}/manifest.txt"
log_info "Creating artifact manifest..."
{
echo "Environment: $env"
echo "Build Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
echo "Build Host: $(hostname)"
echo ""
echo "Artifacts:"
find "$output_dir" -type f -name "*.bin" | sort | while read -r file; do
local size
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
local size_kb=$((size / 1024))
local md5sum_val
md5sum_val=$(md5sum "$file" 2>/dev/null | cut -d' ' -f1 || md5 -q "$file" 2>/dev/null)
echo " - $(basename "$file"): ${size_kb} KB (MD5: ${md5sum_val})"
done
} > "$manifest"
log_info "Manifest created: $manifest"
}
# Function to compress artifacts (optional)
compress_artifacts() {
local output_dir="$1"
local archive_name="$2"
log_info "Compressing artifacts..."
local archive="${output_dir}/${archive_name}.tar.gz"
if tar -czf "$archive" -C "$output_dir" .; then
local size
size=$(stat -f%z "$archive" 2>/dev/null || stat -c%s "$archive" 2>/dev/null)
local size_kb=$((size / 1024))
log_info "Archive created: ${archive_name}.tar.gz (${size_kb} KB)"
else
log_error "Failed to create archive"
return 1
fi
}
# Function to list artifacts
list_artifacts() {
local output_dir="$1"
echo ""
echo "═══════════════════════════════════════"
echo " Prepared Artifacts"
echo "═══════════════════════════════════════"
if [[ -d "$output_dir" ]]; then
find "$output_dir" -type f | sort | while read -r file; do
local size
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
local size_kb=$((size / 1024))
echo " $(basename "$file"): ${size_kb} KB"
done
else
echo " No artifacts found"
fi
echo "═══════════════════════════════════════"
}
# Show usage
usage() {
cat << EOF
Usage: $0 <environment> [OPTIONS]
Prepare firmware artifacts for upload or deployment.
Arguments:
environment PlatformIO environment name
Options:
--deploy Prepare for deployment (rename with environment prefix)
--standard Prepare standard artifacts (no renaming) [default]
--output DIR Output directory [default: generated/artifacts/]
--manifest Create manifest file with artifact metadata
--compress Compress artifacts into tar.gz archive
--help Show this help message
Examples:
$0 esp32dev-all-test
$0 esp32dev-bt --deploy --manifest
$0 theengs-bridge --output build/artifacts --compress
EOF
}
# Main execution
main() {
local environment=""
local mode="standard"
local output_dir="$DEFAULT_OUTPUT_DIR"
local create_manifest_flag=false
local compress_flag=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--deploy)
mode="deploy"
shift
;;
--standard)
mode="standard"
shift
;;
--output)
output_dir="$2"
shift 2
;;
--manifest)
create_manifest_flag=true
shift
;;
--compress)
compress_flag=true
shift
;;
--help|-h)
usage
exit 0
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
environment="$1"
shift
;;
esac
done
# Validate inputs
if [[ -z "$environment" ]]; then
log_error "Environment name is required"
usage
exit 1
fi
# Change to project root
cd "$PROJECT_ROOT"
# Check if build directory exists
if [[ ! -d "${BUILD_DIR}/${environment}" ]]; then
log_error "Build directory not found for environment: $environment"
log_error "Run build_firmware.sh first"
exit 1
fi
# Create output directory
create_output_dir "$output_dir"
# Prepare artifacts based on mode
case "$mode" in
standard)
prepare_standard_artifacts "$environment" "$output_dir" || exit 1
;;
deploy)
prepare_deployment_artifacts "$environment" "$output_dir" || exit 1
;;
*)
log_error "Unknown mode: $mode"
exit 1
;;
esac
# Create manifest if requested
if [[ "$create_manifest_flag" == "true" ]]; then
create_manifest "$environment" "$output_dir"
fi
# Compress if requested
if [[ "$compress_flag" == "true" ]]; then
compress_artifacts "$output_dir" "$environment"
fi
# Show summary
list_artifacts "$output_dir"
log_info "Artifact preparation completed successfully"
}
# Run main if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
+409
View File
@@ -0,0 +1,409 @@
#!/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 "$@"
+197
View File
@@ -0,0 +1,197 @@
#!/bin/bash
# Updates version tags in firmware configuration and JSON files
# Used by: CI/CD pipelines for versioning builds
# Usage: ./set_version.sh <version_tag> [--dev]
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly USER_CONFIG="${PROJECT_ROOT}/main/User_config.h"
readonly LATEST_VERSION_PROD="${SCRIPT_DIR}/latest_version.json"
readonly LATEST_VERSION_DEV="${SCRIPT_DIR}/latest_version_dev.json"
# Load shared configuration (colors, logging functions, paths)
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
# Function to validate version tag
validate_version() {
local version="$1"
if [[ -z "$version" ]] || [[ "$version" == "version_tag" ]]; then
log_error "Invalid version tag: '$version'"
return 1
fi
log_info "Version tag validated: $version"
}
# Function to backup files
backup_file() {
local file="$1"
if [[ -f "$file" ]]; then
cp "$file" "${file}.bak"
log_info "Backed up: $file"
fi
}
# Function to replace version in file
replace_version() {
local file="$1"
local version="$2"
if [[ ! -f "$file" ]]; then
log_warn "File not found: $file (skipping)"
return 0
fi
# Backup before modification
backup_file "$file"
# Replace version_tag placeholder
if sed -i "s/version_tag/${version}/g" "$file"; then
log_info "Updated version in: $file"
else
log_error "Failed to update version in: $file"
return 1
fi
}
# Function to set production version
set_production_version() {
local version="$1"
log_info "Setting PRODUCTION version: $version"
replace_version "$USER_CONFIG" "$version" || return 1
replace_version "$VERSION_JSON" "$version" || return 1
}
# Function to set development version
set_development_version() {
local version="$1"
log_info "Setting DEVELOPMENT version: $version"
replace_version "$USER_CONFIG" "$version" || return 1
replace_version "$VERSION_DEV_JSON" "$version" || return 1
}
# Function to restore backups
restore_backups() {
log_warn "Restoring backups..."
for file in "$USER_CONFIG" "$VERSION_JSON" "$VERSION_DEV_JSON"; do
if [[ -f "${file}.bak" ]]; then
mv "${file}.bak" "$file"
log_info "Restored: $file"
fi
done
}
# Function to clean backups
clean_backups() {
for file in "$USER_CONFIG" "$VERSION_JSON" "$VERSION_DEV_JSON"; do
if [[ -f "${file}.bak" ]]; then
rm "${file}.bak"
fi
done
}
# Show usage
usage() {
cat << EOF
Usage: $0 <version_tag> [OPTIONS]
Update version tags in firmware configuration files.
Arguments:
version_tag Version string to inject (e.g., v1.2.3, abc123, dev-20230101)
Options:
--dev Use development version files (latest_version_dev.json)
--prod Use production version files (latest_version.json) [default]
--help Show this help message
Examples:
$0 v1.2.3 # Production release
$0 abc123 --dev # Development build
$0 \${{ github.sha }} --dev # CI/CD with commit SHA
EOF
}
# Main execution
main() {
local version=""
local is_dev=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--dev)
is_dev=true
shift
;;
--prod)
is_dev=false
shift
;;
--help|-h)
usage
exit 0
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
version="$1"
shift
;;
esac
done
# Validate version
if [[ -z "$version" ]]; then
log_error "Version tag is required"
usage
exit 1
fi
validate_version "$version" || exit 1
# Change to project root
cd "$PROJECT_ROOT"
# Set version with error handling
if [[ "$is_dev" == true ]]; then
set_development_version "$version" || {
restore_backups
exit 1
}
else
set_production_version "$version" || {
restore_backups
exit 1
}
fi
# Clean up backups on success
clean_backups
log_info "Version update completed successfully"
}
# Run main if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
+505
View File
@@ -0,0 +1,505 @@
#!/bin/bash
# CI/CD Site/Documentation Builder
# Builds and deploys VuePress documentation with version management
# Usage: ./scripts/ci_site.sh [OPTIONS]
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly DOCS_DIR="${PROJECT_ROOT}/docs"
readonly VUEPRESS_CONFIG="${DOCS_DIR}/.vuepress/config.js"
readonly VUEPRESS_BUILD_DIR="${DOCS_DIR}/.vuepress/dist"
# 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
# Final output directory for site
readonly SITE_OUTPUT_DIR="${PROJECT_ROOT}/${SITE_DIR}"
# Default values
MODE="prod"
URL_PREFIX="/"
CUSTOM_VERSION=""
VERSION_SOURCE="release"
PREVIEW=false
GENERATE_WEBUPLOADER=true
WEBUPLOADER_ARGS=""
DEPLOY_DIR="."
RUN_PAGESPEED=false
PAGESPEED_URL="https://docs.openmqttgateway.com/"
# Function to check required tools
check_requirements() {
log_info "Checking required tools..."
local missing_tools=()
if ! command -v node >/dev/null 2>&1; then
missing_tools+=("node")
else
local node_version
node_version=$(node --version)
log_info "✓ Node.js ${node_version} found"
fi
if ! command -v npm >/dev/null 2>&1; then
missing_tools+=("npm")
else
local npm_version
npm_version=$(npm --version)
log_info "✓ npm ${npm_version} found"
fi
if ! command -v python3 >/dev/null 2>&1; then
missing_tools+=("python3")
else
local python_version
python_version=$(python3 --version | grep -oP '\d+\.\d+' || echo "unknown")
log_info "✓ Python ${python_version} found"
fi
if ! command -v pip3 >/dev/null 2>&1; then
missing_tools+=("pip3")
else
local pip_version
pip_version=$(pip3 --version | grep -oP '\d+\.\d+' || echo "unknown")
log_info "✓ pip ${pip_version} found"
fi
if [[ ${#missing_tools[@]} -gt 0 ]]; then
log_error "Missing required tools: ${missing_tools[*]}"
return 1
fi
log_info "All required tools are available"
return 0
}
# Function to install dependencies
install_dependencies() {
log_info "Installing dependencies..."
# Upgrade pip first (if pip module is available)
if python3 -m pip --version >/dev/null 2>&1; then
log_info "Upgrading pip..."
python3 -m pip install --upgrade pip --quiet || {
log_warn "Failed to upgrade pip, continuing with existing version..."
}
fi
# Install Python dependencies (without --user if in virtualenv)
log_info "Installing Python dependencies..."
pip3 install requests pandas markdown pytablereader tabulate || {
log_error "Failed to install Python dependencies"
return 1
}
# Install Node dependencies
log_info "Installing Node.js dependencies..."
cd "${PROJECT_ROOT}"
npm install --quiet || {
log_error "Failed to install Node.js dependencies"
return 1
}
log_info "Dependencies installed successfully"
}
# Function to download common config
download_common_config() {
log_info "Downloading common configuration..."
local config_url="https://www.theengs.io/commonConfig.js"
local config_dest="${DOCS_DIR}/.vuepress/public/commonConfig.js"
mkdir -p "$(dirname "$config_dest")"
if curl -sSf -o "$config_dest" "$config_url"; then
log_info "✓ Common config downloaded"
else
log_warn "Failed to download common config, continuing anyway..."
fi
}
# Function to get version
get_version() {
local version=""
if [[ "$VERSION_SOURCE" == "custom" && -n "$CUSTOM_VERSION" ]]; then
version="$CUSTOM_VERSION"
log_info "Using custom version: $version"
elif [[ "$VERSION_SOURCE" == "release" ]]; then
# Try to get latest git tag (simulating GitHub release)
if command -v git >/dev/null 2>&1 && [[ -d "${PROJECT_ROOT}/.git" ]]; then
version=$(git describe --tags --abbrev=0 2>/dev/null || echo "development")
log_info "Using release version from git: $version"
else
version="development"
log_warn "Git not available, using version: $version"
fi
else
# Auto-detect from git
if command -v git >/dev/null 2>&1 && [[ -d "${PROJECT_ROOT}/.git" ]]; then
version=$(git describe --tags --abbrev=0 2>/dev/null || echo "development")
log_info "Using git version: $version"
else
version="development"
log_warn "Git not available, using version: $version"
fi
fi
echo "$version"
}
# Function to set version in config files
set_version() {
local version="$1"
log_info "Setting version: $version"
# Update VuePress config
if [[ -f "$VUEPRESS_CONFIG" ]]; then
sed -i "s|version_tag|${version}|g" "$VUEPRESS_CONFIG"
fi
# Update version JSON file based on version source
if [[ "$VERSION_SOURCE" == "custom" ]]; then
# Custom version updates dev file
local version_file="${SCRIPT_DIR}/latest_version_dev.json"
if [[ -f "$version_file" ]]; then
sed -i "s|version_tag|${version}|g" "$version_file"
fi
else
# Release version updates production file
local version_file="${SCRIPT_DIR}/latest_version.json"
if [[ -f "$version_file" ]]; then
sed -i "s|version_tag|${version}|g" "$version_file"
fi
fi
}
# Function to set URL prefix (base path)
set_url_prefix() {
local url_prefix="$1"
if [[ "$url_prefix" != "/" ]]; then
log_info "Setting URL prefix: $url_prefix"
sed -i "s|base: '/'|base: '${url_prefix}'|g" "$VUEPRESS_CONFIG"
fi
}
# Function to generate board documentation
generate_board_docs() {
log_info "Generating board documentation..."
local generator="${SCRIPT_DIR}/generate_board_docs.py"
if [[ -f "$generator" ]]; then
cd "${PROJECT_ROOT}"
python3 "$generator" || {
log_warn "Board documentation generation failed, continuing..."
}
else
log_warn "Board documentation generator not found, skipping..."
fi
}
# Function to generate WebUploader manifest
generate_webuploader() {
if [[ "$GENERATE_WEBUPLOADER" != true ]]; then
log_info "Skipping WebUploader generation"
return 0
fi
log_info "Generating WebUploader manifest..."
local generator="${SCRIPT_DIR}/gen_wu.py"
if [[ -f "$generator" ]]; then
cd "${PROJECT_ROOT}"
python3 "$generator" $WEBUPLOADER_ARGS || {
log_warn "WebUploader generation failed, continuing..."
}
else
log_warn "WebUploader generator not found, skipping..."
fi
}
# Function to build documentation
build_docs() {
log_info "Building documentation..."
cd "${PROJECT_ROOT}"
# Set Node options for compatibility with newer Node.js versions
export NODE_OPTIONS="--openssl-legacy-provider"
npm run docs:build || {
log_error "Documentation build failed"
return 1
}
if [[ -d "$VUEPRESS_BUILD_DIR" ]]; then
log_info "✓ Documentation built successfully"
# Copy to centralized output directory
log_info "Copying site to: $SITE_OUTPUT_DIR"
mkdir -p "$SITE_OUTPUT_DIR"
rm -rf "${SITE_OUTPUT_DIR}"/*
cp -r "${VUEPRESS_BUILD_DIR}"/* "${SITE_OUTPUT_DIR}/"
log_info " VuePress output: $VUEPRESS_BUILD_DIR"
log_info " Final output: $SITE_OUTPUT_DIR"
else
log_error "Build output directory not found: $VUEPRESS_BUILD_DIR"
return 1
fi
}
# Function to preview documentation
preview_docs() {
log_info "Starting documentation preview..."
if [[ ! -d "$SITE_OUTPUT_DIR" ]]; then
log_error "Build output not found. Run build first."
return 1
fi
log_info "Preview server starting at: http://localhost:8080"
log_info "Press Ctrl+C to stop"
cd "$SITE_OUTPUT_DIR"
python3 -m http.server 8080
}
# Show usage
usage() {
cat << EOF
Usage: $0 [OPTIONS]
Build and deploy OpenMQTTGateway documentation site using VuePress.
This script handles the complete documentation build pipeline including:
- Installing Node.js and Python dependencies
- Downloading shared configuration from theengs.io
- Version management (from git tags or custom version)
- Generating board documentation from environments
- Creating WebUploader manifest for firmware updates
- Building VuePress static site
- Optional local preview or deployment to GitHub Pages
OPTIONS:
--mode MODE
Build mode: 'prod' or 'dev' [default: prod]
- prod: Production documentation with release version
- dev: Development documentation with custom version tag
--url-prefix PATH
URL prefix for documentation routing (VuePress base path) [default: /]
Controls how URLs are generated in the built site.
Examples:
--url-prefix / # Production site at root URL
--url-prefix /dev/ # Development site at /dev/ URL path
Note: Should match --deploy-dir for correct link generation
--custom-version TAG
Override version tag displayed in documentation
Example: --custom-version "v1.8.0-beta"
Note: Automatically sets --version-source to 'custom'
--version-source SOURCE
Source for version information: 'release' or 'custom' [default: release]
- release: Use latest git tag from repository
- custom: Use version from --custom-version parameter
--preview
Start local HTTP server to preview built documentation
Server will run at http://localhost:8080
Press Ctrl+C to stop the preview server
--deploy-dir DIR
Deployment directory on GitHub Pages [default: .]
Controls where files are copied in the gh-pages branch.
Examples:
--deploy-dir . # Deploy to root of gh-pages branch
--deploy-dir dev # Deploy to dev/ folder in gh-pages
Note: Should match --url-prefix for correct site structure
--no-webuploader
Skip WebUploader manifest generation
By default, generates manifest for web-based firmware updates
--webuploader-args ARGS
Additional arguments passed to gen_wu.py script
Example: --webuploader-args "--dev"
--run-pagespeed
Run Google PageSpeed Insights after deployment
Requires APIKEY to be configured in workflow
--pagespeed-url URL
URL to test with PageSpeed Insights
Default: https://docs.openmqttgateway.com/
--help
Show this help message and exit
EXAMPLES:
# Build production documentation with latest release version
$0 --mode prod
# Build and preview development documentation locally
$0 --mode dev --url-prefix /dev/ --preview
# Build with custom version tag
$0 --custom-version "v1.8.0-beta" --version-source custom
# Build without WebUploader manifest
$0 --mode prod --no-webuploader
# Build for dev environment with custom WebUploader args
$0 --mode dev --url-prefix /dev/ --deploy-dir dev --webuploader-args "--dev"
WORKFLOW:
1. Check requirements (Node.js, npm, Python)
2. Install dependencies (npm packages, Python libraries)
3. Download common configuration from theengs.io
4. Determine version (from git tags or custom)
5. Generate board documentation from PlatformIO environments
6. Generate WebUploader manifest (optional)
7. Build VuePress static site
8. Preview (optional)
OUTPUT:
Built documentation will be in: generated/site/
EOF
exit 0
}
# Parse command line arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--mode)
MODE="$2"
shift 2
;;
--url-prefix|--base-path)
URL_PREFIX="$2"
shift 2
;;
--deploy-dir|--destination-dir)
DEPLOY_DIR="$2"
shift 2
;;
--custom-version)
CUSTOM_VERSION="$2"
VERSION_SOURCE="custom"
shift 2
;;
--version-source)
VERSION_SOURCE="$2"
shift 2
;;
--preview)
PREVIEW=true
shift
;;
--no-webuploader)
GENERATE_WEBUPLOADER=false
shift
;;
--webuploader-args)
WEBUPLOADER_ARGS="$2"
shift 2
;;
--run-pagespeed)
RUN_PAGESPEED=true
shift
;;
--pagespeed-url)
PAGESPEED_URL="$2"
shift 2
;;
--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 site build pipeline..."
log_info "Mode: $MODE"
log_info "URL prefix: $URL_PREFIX"
log_info "Deploy directory: $DEPLOY_DIR"
# Check requirements
check_requirements || exit 1
# Install dependencies
install_dependencies || exit 1
# Download common config
download_common_config
# Get and set version
local version
version=$(get_version)
set_version "$version"
# Set URL prefix
set_url_prefix "$URL_PREFIX"
# Generate board documentation
generate_board_docs
# Generate WebUploader manifest
generate_webuploader
# Build documentation
build_docs || exit 1
# Preview if requested
if [[ "$PREVIEW" == true ]]; then
preview_docs
fi
local end_time
end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ""
echo "╔════════════════════════════════════════╗"
echo "║ Site Build Summary ║"
echo "╚════════════════════════════════════════╝"
echo " Mode: $MODE"
echo " Version: $version"
echo " Duration: ${duration}s"
echo " Output: $SITE_OUTPUT_DIR"
echo " Status: SUCCESS ✓"
echo "╚════════════════════════════════════════╝"
}
# Execute main function
main "$@"
+2
View File
@@ -1,3 +1,5 @@
# Common templates and constants for web installer manifest generation
# Used by: scripts/gen_wu.py
import string
mf_temp32 = string.Template('''{
+2
View File
@@ -1,3 +1,5 @@
# Compresses firmware binaries with gzip for OTA updates during build
# Used by: PlatformIO environments (optional, commented in environments.ini)
import gzip
import shutil
import os
+2
View File
@@ -1,3 +1,5 @@
# Creates web installer manifests for ESP Web Tools firmware installation
# Used by: .github/workflows/task-docs.yml
import os
import requests
import json
+2
View File
@@ -1,3 +1,5 @@
# Generates board documentation table from platformio.ini environments
# Used by: .github/workflows/task-docs.yml
import pytablereader as ptr
import pandas as pd
import os
+2
View File
@@ -1,4 +1,6 @@
#!/bin/bash
# Prepares firmware binaries and libraries for GitHub release deployment
# Used by: .github/workflows/release.yml
set -e
echo "renaming bin files with the environment name"
rename -v 's:/:-:g' .pio/build/*/*.bin
+2 -1
View File
@@ -1,4 +1,5 @@
# Replaces BLE library with custom version during PlatformIO build
# Used by: Currently unused (utility script for BLE library replacement)
import shutil
import os
import hashlib