Files
OpenMQTTGateway/scripts/ci_site.sh
Alessandro Staniscia 134c03362c [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.
2026-02-15 14:58:58 -06:00

506 lines
15 KiB
Bash
Executable File

#!/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 "$@"