[SITE] Renew the web board presentation and the ESP32 web upload + [SYS] Security checks (#2277)

* 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.
- Improved internal scripts for better error handling and logging.

UPDATE the web installer manifest generation and update documentation structure
- Enhanced ci_list-env.sh to list environments from a JSON file.
- Replaced  common_wu.py and gen_wu.py scripts with new npm scripts for site generation and previewing on docsgen/gen_wu.js
- Replaced  generate_board_docs.py with docsgen/generated_board_docs.js
- Added new npm scripts for integration of site generation on build phase.
- Created preview_site.js to serve locally generated site over HTTPS with improved error handling.
- Added new CI environments for CI builds in environments.json.
- Deleted lint.yml as part of workflow cleanup.
- Enhanced task-build.yml to include linting as a job and added support for specifying PlatformIO version.
- Improved task-docs.yml to handle versioning more effectively and added clean option.

Enhance documentation
- ADD CLEAR Mark of development version of site
- Updated README.md to include detailed workflow dependencies and relationships using mermaid diagrams.
- Improved development.md with a quick checklist for contributors and clarified the code style guide.
- Enhanced quick_start.md with tips for contributors and streamlined the workflow explanation.

LINT FIX
- Refined User_config.h for better formatting consistency.
- Adjusted blufi.cpp and gatewayBT.cpp for improved code readability and consistency in formatting.
- Updated gatewaySERIAL.cpp and mqttDiscovery.cpp to enhance logging error messages.
- Improved sensorDS1820.cpp for better logging of device information.

Add security scan workflows for vulnerability detection

Add SBOM generation and upload to release workflow; update security scan summary handling

Add shellcheck suppor + FIX shellcheck warning

Enhance documentation for CI/CD scripts and workflows, adding details for security scanning and SBOM generation processes

Fix formatting and alignment in BLE connection handling

Reviewed the full web board presentation and the ESP32 web upload. The project uses a modern pattern where data is divided from the presentation layer.

- Removed the `generate_board_docs` script.
- Updated the `gen_wu` script in order to generate `boards-info.json`: the fail that containe all information about the configuration
- Created and isolate the file `boards-info.js` to streamline the parsing of PlatformIO dependencies, modules, environments and improve the handling of library information.
- Introduced vuepress component `BoardEnvironmentTable.vue` that render `boards-info.json` as UI card component
- Introduced vuepress component `FlashEnvironmentSelector.vue` that render a selectred environment from  `boards-info.json` and provide esp-web-upload feature on it
- Introduced a new board page `board-selector.md` for improved firmware selection.
- Updated `web-install.md` to enhance the firmware upload process, including a new board environment table.
- Enhanced custom descriptions in `environments.ini` to include HTML links for better user guidance and board image link

Add CC1101 initialization improvements and logging enhancements
Add installation step for PlatformIO dependencies in documentation workflow

Remove ci_set_version.sh script and associated versioning functionality

* Fix comment provisined

Fix PlatformIO version input reference in documentation workflow

Remove outdated Squeezelite-ESP32 installer documentation
This commit is contained in:
Alessandro Staniscia authored and GitHub committed 2026-03-09 07:47:30 -05:00
1 parent 5f0966c2c1
commit 98481c5145
73 files changed
+5985 -2447

No files matched your search

+427 -212
View File
@@ -11,11 +11,13 @@ This documentation describes the CI/CD scripts used to build OpenMQTTGateway fir
- [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)
- [ci.sh qa - Code Quality Checks](#cish-qa---code-quality-checks)
- [ci.sh security - Security Vulnerability Scan](#cish-security---security-vulnerability-scan)
- [Internal Scripts](#internal-scripts)
- [ci_set_version.sh](#ci_set_versionsh)
- [ci_list-env.sh](#ci_list-envsh)
- [ci_build_firmware.sh](#ci_build_firmwaresh)
- [ci_prepare_artifacts.sh](#ci_prepare_artifactssh)
- [ci_security.sh](#ci_securitysh)
- [ci_00_config.sh](#ci_00_configsh)
- [Python Helper Scripts](#python-helper-scripts)
- [generate_board_docs.py](#generate_board_docspy)
@@ -23,6 +25,7 @@ This documentation describes the CI/CD scripts used to build OpenMQTTGateway fir
- [Environment Variables](#environment-variables)
- [Exit Codes](#exit-codes)
- [Environment Detection](#environment-detection)
- [GitHub Actions Workflows Integration](#github-actions-workflows-integration)
@@ -32,27 +35,28 @@ This documentation describes the CI/CD scripts used to build OpenMQTTGateway fir
```
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)
├── build → ci_build.sh → ci_build_firmware.sh
│ → ci_prepare_artifacts.sh (when --deploy-ready)
├── site → ci_site.sh
├── qa → ci_qa.sh
├── security → ci_security.sh (vulnerability scanning with Trivy)
├── list-env → ci_list-env.sh
└── all → qa + build (all envs with --mode) + 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_00_config.sh` | Shared configuration loader | ci.sh |
| `ci_qa.sh` | Quality assurance and shellcheck | ci.sh qa |
| `ci_security.sh` | Security vulnerability scanning with Trivy | ci.sh security |
| `ci_list-env.sh` | List PlatformIO environments (fast JSON or full scan) | ci.sh list-env / direct |
| `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 |
| `ci_prepare_artifacts.sh` | Artifact packaging (when requested) | ci_build.sh |
### Output Structure
@@ -67,16 +71,14 @@ Build outputs are organized in the project root:
generated/
├── artifacts/ # Packaged firmware artifacts
└── site/ # Built documentation (VuePress output)
scripts/
├── latest_version.json # Production version metadata
└── latest_version_dev.json # Development version metadata
├── site/ # Built documentation (VuePress output)
└── reports/ # Security scan and SBOM reports
└── sbom/ # SBOM in CycloneDX and SPDX formats
```
## Commands
`ci.sh` is the main Entry Point. Command dispatcher that routes to specialized scripts.
`ci.sh` is the main entry point. It dispatches to the specialized scripts.
**Usage:**
```bash
@@ -84,115 +86,90 @@ scripts/
```
**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)
- `build` - Build firmware for a PlatformIO environment (optionally prepare artifacts)
- `site` or `docs` - Build documentation site
- `qa` or `lint` - Run formatting and shellcheck checks
- `security` - Scan for security vulnerabilities using Trivy (filesystem, container images)
- `all` or `pipeline` - Run qa → build (all environments) → site with injected mode
- `list-env` - List available PlatformIO environments (JSON fast list or full ini scan)
**Examples:**
```bash
# Get Help
# Help per command
./scripts/ci.sh build --help
./scripts/ci.sh qa --help
./scripts/ci.sh site --help
./scripts/ci.sh qa --help
./scripts/ci.sh security --help
./scripts/ci.sh list-env --help
# Build firmware
./scripts/ci.sh build esp32dev-ble --mode dev
./scripts/ci.sh build esp32dev-all-test --version v1.8.0 --deploy-ready
./scripts/ci.sh build esp32dev-all-test --mode dev
./scripts/ci.sh build esp32dev-bt --version v1.8.0 --deploy-ready --output generated/artifacts
# Build documentation
./scripts/ci.sh site --mode prod
./scripts/ci.sh site --mode dev --url-prefix /dev/
# Build docs
./scripts/ci.sh site --mode prod --url-prefix /
./scripts/ci.sh site --mode dev --preview
# Check code formatting
# QA (formatting + shellcheck)
./scripts/ci.sh qa --check
./scripts/ci.sh qa --fix
./scripts/ci.sh qa --fix --verbose
# Run complete pipeline
./scripts/ci.sh all esp32dev-ble --version v1.8.0
./scripts/ci.sh all esp32dev-ble --no-site
# Security scanning
./scripts/ci.sh security --scan-type fs --severity HIGH,CRITICAL
./scripts/ci.sh security --scan-type fs --generate-sbom
# Full pipeline (qa + build all envs + site)
./scripts/ci.sh all --mode dev
./scripts/ci.sh all --mode prod --preview
```
**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.
Runs the build pipeline (tool checks → PlatformIO build → optional artifact packaging).
**Usage:**
```bash
./scripts/ci.sh build <environment> [OPTIONS]
```
**Required Arguments:**
- `<environment>` - PlatformIO environment name (e.g., esp32dev-ble, nodemcuv2-rf)
**Required Argument:**
- `<environment>` PlatformIO environment name (e.g., esp32dev-ble)
**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
- `--mode <prod|dev>` Build mode (default: prod). `dev` enables OTA flags in the PlatformIO build.
- `--deploy-ready` Copy/rename build outputs and libs via ci_prepare_artifacts.sh.
- `--version [TAG]` Set version used for `OMG_VERSION` and artifact folder naming. If TAG is omitted the script auto-generates (CI: BUILD_NUMBER/GIT_COMMIT, local: timestamp).
- `--output <dir>` Output directory for packaged artifacts (only used when `--deploy-ready` is set, default `generated/artifacts`).
- `--skip-verification` Skip the tool availability checks.
- `--clean` Clean the PlatformIO environment before building.
- `--verbose` Verbose PlatformIO output.
- `--help` Show help.
**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
```
**Behavior:**
- Tool check verifies python3, platformio, git (can be skipped).
- Builds via ci_build_firmware.sh (adds `--dev-ota` when `--mode dev`).
- Packaging runs only when `--deploy-ready` is provided; artifacts land under `generated/artifacts/firmware_build/` with env-prefixed filenames plus zipped libraries.
<!-- - Packaging runs only when `--deploy-ready` is provided; artifacts land under `generated/artifacts[/<version>]/firmware_build/` with env-prefixed filenames plus zipped libraries. -->
**Examples:**
```bash
# Development build
# Dev build with OTA flags
./scripts/ci.sh build esp32dev-ble --mode dev
# Production build with version
./scripts/ci.sh build esp32dev-ble --version v1.8.0 --mode prod
# Prod build with auto-version and deployable artifacts
./scripts/ci.sh build esp32dev-bt --version --deploy-ready --output generated/artifacts
# Deploy-ready build
./scripts/ci.sh build esp32dev-all-test --version v1.8.0 --deploy-ready
# Clean build with verbose output
# Clean + verbose build
./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.
Builds the VuePress documentation site.
**Usage:**
```bash
@@ -200,75 +177,36 @@ Builds VuePress documentation website with version management and WebUploader ma
```
**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
- `--mode <prod|dev>` Build mode (default: dev).
- `--url-prefix <path>` Base URL path for links (e.g., '/' for root, '/dev/' for dev) (default: /dev/).
- `--version <tag>` Version string written to meta.json (default: edge).
- `--preview` Start the local HTTPS preview server after building (https://localhost:8443).
- `--clean` Remove generated/site folder before building.
- `--insecure-curl` Allow curl to skip TLS verification when downloading common config.
- `--help` Show help.
**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]
```
**Behavior:**
- Checks for node, npm, openssl; installs npm deps; downloads commonConfig.js.
- Writes docs/.vuepress/meta.json with mode/url_prefix/version; builds via `npm run docs:build`.
- Preview mode runs `npm run site:preview`.
**Examples:**
```bash
# Production documentation
./scripts/ci.sh site --mode prod
# Production build
./scripts/ci.sh site --mode prod --url-prefix /
# Development documentation with custom version
./scripts/ci.sh site --mode dev --version-source custom --custom-version "DEVELOPMENT SHA:abc123"
# Development build with preview
./scripts/ci.sh site --mode dev --url-prefix /dev/ --version edge --preview
# 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
# Clean then build with custom version
./scripts/ci.sh site --clean --version 1.8.0
```
**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
### ci.sh qa - Code Quality Checks
Checks and fixes code formatting using clang-format.
Checks and fixes code formatting using clang-format and runs shellcheck on shell scripts.
**Usage:**
```bash
@@ -276,13 +214,16 @@ Checks and fixes code formatting using clang-format.
```
**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
- `--check` Check formatting only (default)
- `--fix` Apply formatting in place
- `--format` Run only format checks
- `--shellcheck` Run shellcheck on shell scripts in scripts/ directory
- `--all` Future hook to run all QA checks (current implementation runs formatting + shellcheck)
- `--source <dir>` Source directory for formatting checks (default: main)
- `--extensions <list>` File extensions for formatting (comma-separated, default: h,ino,cpp)
- `--clang-format-version <ver>` clang-format version to use (default: 9)
- `--verbose` Verbose output
- `--help` Show help
**Execution Flow:**
```
@@ -290,27 +231,35 @@ 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
│ │ ├─> find_files() - Locate files matching extensions in source dir
│ │ └─> check_formatting() - Run clang-format --dry-run --Werror
│ │ └─> Report files with formatting issues
│ │
│ └─> shellcheck_check() - Find and scan shell scripts
│ ├─> find_shell_scripts() - Locate *.sh files in scripts/ directory
│ └─> run_shellcheck() - Run shellcheck on found scripts
│ └─> Report shell script issues
│
└─> Exit code: 0 (pass) or 1 (formatting issues found)
└─> Exit code: 0 (pass) or 1 (issues found)
```
**Examples:**
```bash
# Check formatting (CI mode)
# Check both formatting and shellcheck (default)
./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 formatting for specific directory
./scripts/ci.sh qa --check --format --source lib
# Check only .h and .ino files
./scripts/ci.sh qa --check --extensions h,ino
# Check only shellcheck for shell scripts
./scripts/ci.sh qa --check --shellcheck
# Check with verbose output
./scripts/ci.sh qa --check --verbose
@@ -321,46 +270,201 @@ ci.sh qa --check --source main --extensions h,ino
**Required Tools:**
- clang-format (version specified, default: 9)
- Install: `sudo apt-get install clang-format-9`
- shellcheck (for shell script linting)
- Install: `sudo apt-get install shellcheck`
**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)
- Check mode: Lists files with formatting issues and shell script errors, shows diffs
- Fix mode: Modifies formatting in-place and reports changes
- Exit code 0: All checks passed (proper formatting and no shellcheck errors)
- Exit code 1: Issues found (formatting or shellcheck violations)
---
### ci.sh security - Security Vulnerability Scan
Scans the project for security vulnerabilities using Trivy and generates Software Bill of Materials (SBOM).
**Usage:**
```bash
./scripts/ci.sh security [OPTIONS]
```
**Options:**
- `--scan-type <fs|config|image>` Type of scan (default: fs)
- `fs` - Filesystem scan (default, scans for vulnerabilities and misconfigurations)
- `config` - Configuration scan only
- `image` - Container image scan
- `--severity <levels>` Severity levels to report (comma-separated: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default: HIGH,CRITICAL)
- `--scan-path <path>` Path to scan (default: current directory `.`)
- `--generate-sbom` Generate Software Bill of Materials in CycloneDX and SPDX formats (default: true)
- `--exit-code <0|1>` Exit code when vulnerabilities found (0=continue, 1=fail) (default: 0)
- `--upload-to-security-tab` Upload SARIF report to GitHub Security tab (GitHub Actions only, default: true)
- `--verbose` Verbose output
- `--help` Show help
**Behavior:**
- Installs Trivy if not present
- Scans filesystem or configuration for known vulnerabilities
- Generates multiple report formats: SARIF, JSON, table summary
- Creates SBOM in CycloneDX and SPDX formats when `--generate-sbom` is enabled
- Reports are saved to `generated/reports/` directory
- Summary is appended to GitHub job summary when running in GitHub Actions
- Uploads SARIF to GitHub Security tab for dashboard visibility
**Output Structure:**
```
generated/reports/
├── trivy-results.sarif # SARIF format (for GitHub Security tab)
├── trivy-results.json # JSON format (detailed results)
├── security-summary.md # Markdown summary
└── sbom/
├── sbom.cyclonedx.json # CycloneDX format
└── sbom.spdx.json # SPDX format
```
**Examples:**
```bash
# Scan filesystem for HIGH and CRITICAL vulnerabilities
./scripts/ci.sh security --scan-type fs --severity HIGH,CRITICAL
# Full scan with all severity levels and SBOM generation
./scripts/ci.sh security --scan-type fs --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --generate-sbom
# Scan specific path with verbose output
./scripts/ci.sh security --scan-path ./lib --verbose
# Configuration scan only
./scripts/ci.sh security --scan-type config
# Scan and fail on vulnerabilities
./scripts/ci.sh security --exit-code 1
```
**Required Tools:**
- Trivy (vulnerability scanner)
- Install: `wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -` && `echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list` && `sudo apt-get update && sudo apt-get install -y trivy`
**Output:**
- Detailed SARIF and JSON reports for integration with tools
- Human-readable Markdown summary with findings count
- SBOM artifacts for supply chain tracking
- GitHub Security tab integration when in GitHub Actions
- Exit code 0: Scan completed (vulnerabilities may have been found)
- Exit code 1: Scan failed or critical vulnerabilities found (only if `--exit-code 1`)
---
### ci.sh all - Complete Pipeline
Runs the complete CI/CD pipeline (qa → build all environments → site) with mode injected to all steps.
**Usage:**
```bash
./scripts/ci.sh all --mode <dev|prod> [--preview]
```
**Required Options:**
- `--mode <dev|prod>` Build mode (required). Injected to all pipeline steps:
- `qa`: Always runs `--check`
- `build`: Passes mode to all PlatformIO environments (dev enables OTA flags)
- `site`: Passes mode for documentation generation
**Optional Options:**
- `--preview` Start local HTTPS preview server at https://localhost:8443 after building the site
- `--help` Show help
**Behavior:**
- No environment argument needed; builds **all** available environments
- All three steps (qa, build, site) receive the same `--mode` value
- If any step fails, the pipeline aborts
- Site build warnings do not abort the pipeline (continues with success status)
**Execution Flow:**
```
ci.sh all --mode dev --preview
│
├─> Step 1: ci_qa.sh --check
│ └─> Exit on failure
│
├─> Step 2: ci_build.sh --mode dev (builds all environments)
│ └─> Exit on failure
│
└─> Step 3: ci_site.sh --mode dev --preview
├─> Build documentation
└─> Start preview server at https://localhost:8443
```
**Examples:**
```bash
# Complete pipeline in dev mode
./scripts/ci.sh all --mode dev
# Complete pipeline in prod mode with site preview
./scripts/ci.sh all --mode prod --preview
# Help for this command
./scripts/ci.sh all --help
```
**Output:**
- Step-by-step progress messages for each pipeline phase
- Final summary showing mode, preview status, duration, and overall status
- Exit code 0: All steps successful
- Exit code 1: Any step failed
---
### ci.sh list-env - List Environments
Lists PlatformIO environments available to build.
**Usage:**
```bash
./scripts/ci.sh list-env [--full]
```
**Options:**
- Default: read .github/workflows/environments.json (requires jq) and show curated list.
- `--full` Parse platformio.ini and environments.ini for an exhaustive list (skips *-test and *-all-).
- `--help` Show help.
**Examples:**
```bash
./scripts/ci.sh list-env
./scripts/ci.sh list-env --full
```
---
## Internal Scripts
These scripts are called by the main orchestrators. It can be invoked directly but is not raccomanded.
These scripts are called by the main orchestrators. They can be run directly for troubleshooting, but the preferred entrypoints are the ci.sh commands.
### ci_set_version.sh
### ci_list-env.sh
Injects version string into firmware configuration files.
Lists PlatformIO environments for OpenMQTTGateway.
**Called By:** `ci_build.sh`
**Called By:** `ci.sh list-env` or direct call
**Usage:**
```bash
./scripts/ci_set_version.sh <version> [--dev]
./scripts/ci_list-env.sh [--full]
```
**Arguments:**
- `<version>` - Version string to inject (e.g., v1.8.0 or abc123)
- `--dev` - Development mode (updates latest_version_dev.json)
**Options:**
- Default: read .github/workflows/environments.json for a curated list (needs jq)
- `--full` - Parse platformio.ini and environments.ini for all envs except *-test and *-all-
- `-h|--help` - Show help
**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)
**Output:**
- Shows sorted environments in columns and prints the total count
**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
---
**Examples:**
```bash
./scripts/ci_list-env.sh
./scripts/ci_list-env.sh --full
```
### ci_build_firmware.sh
@@ -377,14 +481,17 @@ Executes PlatformIO build for specified environment.
- `<environment>` - PlatformIO environment name
**Options:**
- `--version <tag>` - Set OMG_VERSION for the build (passed through from ci_build.sh)
- `--dev-ota` - Enable development OTA (sets PLATFORMIO_BUILD_FLAGS)
- `--clean` - Clean before build
- `--verbose` - Verbose PlatformIO output
- `--no-verify` - Skip artifact verification after build
**Environment Variables Set:**
- `PYTHONIOENCODING=utf-8`
- `PYTHONUTF8=1`
- `PLATFORMIO_BUILD_FLAGS="-DDEVELOPMENTOTA=true"` (when --dev-ota)
- `OMG_VERSION` (when --version is provided)
**PlatformIO Command:**
```bash
@@ -400,7 +507,7 @@ platformio run -e <environment> [--verbose]
### ci_prepare_artifacts.sh
Packages firmware binaries from PlatformIO build directory.
Packages firmware binaries and libraries from a PlatformIO build directory; can also only create source archive when no environment is provided.
**Called By:** `ci_build.sh`
@@ -410,29 +517,19 @@ Packages firmware binaries from PlatformIO build directory.
```
**Arguments:**
- `<environment>` - PlatformIO environment name
- `<environment>` - PlatformIO environment name (optional; if omitted only source archive is created)
**Options:**
- `--deploy` - Use deployment naming (prefix with environment name)
- `--output <dir>` - Output directory (default: generated/artifacts)
- `--output <dir>` Output directory (default: generated/artifacts)
- `--version <tag>` Append a version subfolder inside the output directory
- `--clean` Clean output directory before writing
- `--help` Show help
**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/`
- If `version` is provided, outputs go to `<output>/<version>/firmware_build/`; otherwise to `<output>/firmware_build/`.
- With an environment: copies and renames firmware.bin/partitions.bin/bootloader.bin/boot_app0.bin to `<env>-*.bin`, then zips libraries for that env into `*-libraries.tgz`.
- Without an environment: only creates `OpenMQTTGateway_sources.tgz` from `main` and `LICENSE.txt`.
- Lists the prepared artifacts and their sizes at the end.
---
@@ -452,6 +549,7 @@ Shared configuration and helper functions for all CI scripts.
- `BUILD_DIR=".pio/build"` - PlatformIO build directory
- `ARTIFACTS_DIR="generated/artifacts"` - Artifact output directory
- `SITE_DIR="generated/site"` - Documentation output directory
- `REPORTS_DIR="generated/reports"` - Security scan and quality reports directory
**Logging Functions:**
```bash
@@ -463,9 +561,74 @@ log_success "message" # Green [SUCCESS] prefix
---
### ci_security.sh
Performs security vulnerability scanning and Software Bill of Materials (SBOM) generation using Trivy.
**Called By:** `ci.sh security`
**Usage:**
```bash
./scripts/ci_security.sh [OPTIONS]
```
**Options:**
- `--scan-type <fs|config|image>` Type of scan (default: fs)
- `fs` - Filesystem scan (default)
- `config` - Configuration scan
- `image` - Container image scan
- `--severity <levels>` Severity levels (comma-separated: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default: HIGH,CRITICAL)
- `--scan-path <path>` Path to scan (default: .)
- `--generate-sbom` Generate SBOM (default: true)
- `--exit-code <0|1>` Exit code behavior (0=continue, 1=fail) (default: 0)
- `--upload-to-security-tab` Upload SARIF to GitHub (default: true)
- `--verbose` Verbose output
- `--help` Show help
**Behavior:**
- Ensures Trivy is installed via package manager
- Creates `generated/reports/` directory structure
- Runs Trivy with specified parameters
- Generates SARIF, JSON, and table formats
- Creates SBOM in CycloneDX and SPDX formats (when enabled)
- Uploads SARIF to GitHub Security tab when `GITHUB_TOKEN` and `--upload-to-security-tab` are set
- Appends summary to GitHub job summary if in GitHub Actions
- Validates critical vulnerabilities and exits with code 1 if found and `--exit-code 1` is set
**Output Files:**
- `generated/reports/trivy-results.sarif` - SARIF format for GitHub integration
- `generated/reports/trivy-results.json` - Full JSON results
- `generated/reports/security-summary.md` - Human-readable summary
- `generated/reports/sbom/sbom.cyclonedx.json` - CycloneDX SBOM
- `generated/reports/sbom/sbom.spdx.json` - SPDX SBOM
**Exit Codes:**
- `0` - Success (vulnerabilities may have been found)
- `1` - Scan failed, critical vulnerabilities found (only if `--exit-code 1`), or missing dependencies
**Trivy Integration:**
- Scans for known vulnerabilities in dependencies
- Detects misconfigurations and insecure practices
- Generates compliant SBOM artifacts
- Provides detailed reporting in multiple formats
**Example:**
```bash
# Scan filesystem with severity filter
./scripts/ci_security.sh --scan-type fs --severity HIGH,CRITICAL
# Local scan with SBOM and JSON output
./scripts/ci_security.sh --scan-type fs --generate-sbom --verbose
# In GitHub Actions with security tab upload
./scripts/ci_security.sh --scan-type fs --severity HIGH,CRITICAL --upload-to-security-tab
```
---
## Python Helper Scripts
Other scripts are present and used as internal scripts and it's used as retrocompatibility. Below the lists:
Legacy helper scripts are kept for compatibility; they are not called by the current ci_site.sh flow. Below the list:
- `generate_board_docs.py`
- `gen_wu.py`
@@ -473,7 +636,7 @@ Other scripts are present and used as internal scripts and it's used as retrocom
Auto-generates board-specific documentation pages from platformio.ini.
**Called By:** `ci_site.sh`
**Called By:** Not invoked by current ci_site.sh (legacy helper)
**Usage:**
```bash
@@ -498,7 +661,7 @@ python3 ./scripts/generate_board_docs.py
Generates WebUploader manifest for OTA firmware updates.
**Called By:** `ci_site.sh`
**Called By:** Not invoked by current ci_site.sh (legacy helper)
**Usage:**
```bash
@@ -527,10 +690,10 @@ python3 ./scripts/gen_wu.py [--dev] [repository]
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
- `CI`/`BUILD_NUMBER`/`GIT_COMMIT`: Used by ci_build.sh to auto-generate version when `--version` flag has no tag
- `PYTHONIOENCODING=utf-8`, `PYTHONUTF8=1`: Python encoding settings set by ci_build_firmware.sh
- `PLATFORMIO_BUILD_FLAGS`: Set to include development OTA flag when `--dev-ota` is used
- `OMG_VERSION`: Set when `--version` is passed to ci_build.sh/ci_build_firmware.sh
---
@@ -567,6 +730,58 @@ CI/CD environments typically set:
- `BUILD_NUMBER` (build number)
- `GIT_COMMIT` (commit hash)
---
## GitHub Actions Workflows Integration
The CI scripts integrate with GitHub Actions workflows in `.github/workflows/`:
### task-lint.yml
- Reusable workflow that runs `ci.sh qa --check`
- Installs clang-format and shellcheck
- Validates code formatting and shell script quality
- Can be called with custom source directory and file extensions
### task-build.yml
- Main build workflow orchestrator
- Calls task-lint.yml for code quality checks
- Calls task-security-scan.yml for vulnerability scanning
- Builds firmware for all or specified environments
- Prepares and uploads build artifacts
- Supports matrix builds for multiple environments
- Manages build artifact retention
### task-security-scan.yml
- Reusable security scanning workflow
- Installs Trivy vulnerability scanner
- Calls `ci_security.sh` with configurable parameters
- Generates SARIF, JSON, and SBOM reports
- Uploads SARIF to GitHub Security tab for code scanning dashboard
- Fails build on critical vulnerabilities when configured
- Uploads SBOM artifacts for supply chain tracking
### security-scan.yml
- Scheduled security scanning (runs weekly by default)
- Triggered manually with input parameters
- Allows filtering by severity level
- Configurable exit behavior (fail or continue)
- Optional SBOM generation and upload
**Workflow Dependencies:**
```
task-build.yml
├─> task-lint.yml (linting)
├─> task-security-scan.yml (security scanning)
└─> Build environment matrix (firmware compilation)
```
**Key Features:**
- Parallel linting and security scans
- Artifact retention policies
- GitHub Security tab integration
- Detailed build reports
- SBOM generation for compliance
- Support for custom build parameters
---
+197 -43
View File
@@ -6,8 +6,9 @@
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_DIR
# Load shared configuration (colors, logging functions, paths)
if [[ -f "${SCRIPT_DIR}/ci_00_config.sh" ]]; then
@@ -36,7 +37,9 @@ Commands:
build Build firmware for specified environment
site Build and deploy documentation/website
qa Run quality assurance checks (linting, formatting)
security Run security vulnerability scan using Trivy
all Run complete pipeline (qa + build + site)
list-env List available environments for building firmware
Examples:
# Build firmware
@@ -51,78 +54,217 @@ Examples:
$0 qa --check
$0 qa --fix
# Run complete pipeline
$0 all esp32dev-bt --version v1.8.0
# Run security scan
$0 security
$0 security --scan-type config --exit-code 1
$0 security --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
# Run complete pipeline (all envs, mode required)
$0 all --mode dev
$0 all --mode prod --preview
$0 all --mode dev -e esp32dev-bt
# List available environments
$0 list-env
Get help for specific commands:
$0 build --help
$0 site --help
$0 qa --help
$0 security --help
$0 list-env --help
EOF
exit 0
}
# Function to run build pipeline
run_build_pipeline() {
log_info "Executing build pipeline..."
"${SCRIPT_DIR}/ci_build.sh" "$@"
# Function to get list of environments to build
# Uses ci_list-env.sh (no parameters) and returns one env per line
get_environments() {
# Run the curated list script and normalize output
"${SCRIPT_DIR}/ci_list-env.sh" \
| sed -r 's/\x1B\[[0-9;]*[mK]//g' \
| tr '\t ' '\n' \
| sed '/^\s*$/d' \
| grep -E '^[A-Za-z0-9._-]+$' \
| sort -u
}
# 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
# Function to run complete pipeline using the underlying scripts
# Usage: run_all_pipeline --mode <dev|prod> [--preview]
run_all_pipeline() {
local start_time
start_time=$(date +%s)
log_info "Starting complete CI/CD pipeline..."
local mode=""
local preview=false
local env_override=""
local version=""
local do_clean=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--mode)
if [[ -z "${2:-}" ]]; then
log_error "--mode requires an argument (dev or prod)"
return 1
fi
mode="$2"
if [[ "$mode" != "dev" && "$mode" != "prod" ]]; then
log_error "Invalid mode: $mode. Must be 'dev' or 'prod'"
return 1
fi
shift 2
;;
--clean)
do_clean=true
shift
;;
--preview)
preview=true
shift
;;
-e|--env)
if [[ -z "${2:-}" ]]; then
log_error "-e|--env requires an environment name"
return 1
fi
env_override="$2"
shift 2
;;
-v|--version)
if [[ -z "${2:-}" ]]; then
log_error "-v|--version requires a version string"
return 1
fi
version="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 all --mode <dev|prod> [--preview]"
echo ""
echo "Options:"
echo " --mode <dev|prod> Build mode (required)"
echo " --preview Optional Show site in local at http://localhost:8443"
echo " -e, --env <name> Optional Build only the specified environment"
return 0
;;
*)
log_error "Unknown option: $1"
return 1
;;
esac
done
# Validate mode is provided
if [[ -z "$mode" ]]; then
log_error "--mode is required. Usage: $0 all --mode <dev|prod> [--preview]"
return 1
fi
log_info "Starting complete CI/CD pipeline (mode: $mode, preview: $preview)..."
echo ""
# Step 1: Quality Assurance
log_info "═══ Step 1/3: Quality Assurance ═══"
run_qa_pipeline --check || {
log_info "RUN: ---> ${SCRIPT_DIR}/ci_qa.sh --check"
if ! "${SCRIPT_DIR}/ci_qa.sh" --check; then
log_error "QA checks failed. Pipeline aborted."
return 1
}
fi
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..."
}
# Get list of environments
local -a environments
if [[ -n "$env_override" ]]; then
log_info "Using single environment override: $env_override"
environments=("$env_override")
else
log_info "Skipping site build (--no-site flag)"
mapfile -t environments < <(get_environments)
fi
if [[ ${#environments[@]} -eq 0 ]]; then
log_error "No environments found to build"
return 1
fi
log_info "Found ${#environments[@]} environments to build"
echo ""
local build_count=0
local failed_builds=()
local build_args=()
if [[ -n "$version" ]]; then
build_args+=("--version" "$version")
log_info "Using version override: $version"
fi
build_args+=("--mode" "$mode")
build_args+=("--deploy-ready")
if [[ "$do_clean" == true ]]; then
build_args+=("--clean")
fi
for env in "${environments[@]}"; do
((++build_count))
log_info "[$build_count/${#environments[@]}] Building: $env"
set +e
log_info "RUN: ---> ${SCRIPT_DIR}/ci_build.sh" "$env" "${build_args[@]}"
"${SCRIPT_DIR}/ci_build.sh" "$env" "${build_args[@]}"
local rc=$?
set -e
if [[ $rc -ne 0 ]]; then
log_error "Build failed for environment: $env"
failed_builds+=("$env")
fi
done
echo ""
if [[ ${#failed_builds[@]} -gt 0 ]]; then
log_error "Build failed for ${#failed_builds[@]} environment(s):"
printf ' - %s\n' "${failed_builds[@]}"
return 1
fi
log_success "All environments built successfully (${#environments[@]} total)"
echo ""
# Step 3: Build Site
log_info "═══ Step 3/3: Build Documentation ═══"
local site_args=("--mode" "$mode")
if [[ "$preview" == true ]]; then
site_args+=("--preview")
fi
if [[ -n "$version" ]]; then
site_args+=("--version" "$version")
fi
if [[ "$do_clean" == true ]]; then
site_args+=("--clean")
fi
log_info "RUN: --->${SCRIPT_DIR}/ci_site.sh" "${site_args[@]}"
if ! "${SCRIPT_DIR}/ci_site.sh" "${site_args[@]}"; then
log_warn "Site build failed, but continuing..."
fi
echo ""
local end_time
end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ""
echo "╔════════════════════════════════════════╗"
echo "║ Complete Pipeline Summary ║"
echo "╚════════════════════════════════════════╝"
echo " Mode: $mode"
echo " Preview: $preview"
echo " Total Duration: ${duration}s"
echo " Status: SUCCESS ✓"
echo "╚════════════════════════════════════════╝"
@@ -151,14 +293,26 @@ main() {
# Route to appropriate pipeline
case "$command" in
build)
run_build_pipeline "$@"
log_info "Executing build pipeline..."
"${SCRIPT_DIR}/ci_build.sh" "$@"
;;
site|docs)
run_site_pipeline "$@"
log_info "Executing site pipeline..."
"${SCRIPT_DIR}/ci_site.sh" "$@"
;;
qa|lint)
run_qa_pipeline "$@"
log_info "Executing QA pipeline..."
"${SCRIPT_DIR}/ci_qa.sh" "$@"
;;
security)
log_info "Executing security scan..."
"${SCRIPT_DIR}/ci_security.sh" "$@"
;;
list-env)
log_info "Executing list-env pipeline..."
"${SCRIPT_DIR}/ci_list-env.sh" "$@"
;;
all|pipeline)
run_all_pipeline "$@"
;;
+6 -33
View File
@@ -1,39 +1,16 @@
#!/bin/bash
# shellcheck disable=SC2034
# 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"
ARTIFACTS_DIR="generated/artifacts"
SITE_DIR="generated/site"
REPORTS_DIR="generated/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
@@ -50,9 +27,5 @@ readonly NC='\033[0m' # No Color
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_success() { echo -e "${GREEN}[SUCCESS]${NC} $*" >&2; }
log_step() { echo -e "${BLUE}[STEP]${NC} $*" >&2; }
# Advanced Options
ENABLE_CCACHE="false"
CCACHE_DIR=".ccache"
MAX_BUILD_JOBS="4"
+50 -75
View File
@@ -6,8 +6,10 @@
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
# Load shared configuration (colors, logging functions, paths)
if [[ -f "${SCRIPT_DIR}/ci_00_config.sh" ]]; then
@@ -71,7 +73,6 @@ verify_build_tools() {
log_info "Verifying required build tools..."
local missing_tools=()
local version_mismatch=()
# Check Python
if ! command_exists python3; then
@@ -130,39 +131,6 @@ cleanup_on_error() {
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
@@ -174,15 +142,14 @@ 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
'dev' enables OTA and development features
--deploy-ready Prepare for deployment (renamed artifacts)
--version [TAG] Set version tag (if TAG omitted, auto-generated)
--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:
@@ -191,9 +158,6 @@ Environment Variables:
GIT_COMMIT Git commit hash for versioning
Examples:
# List available environments
$0 --list-envs
# Local development build
$0 esp32dev-all-test --mode dev
@@ -212,7 +176,7 @@ main() {
local version=""
local set_version=false
local mode=""
local deploy=false
local prepare_for_deploy=false
local output_dir=""
local skip_verification=false
local clean=false
@@ -246,7 +210,7 @@ main() {
shift 2
;;
--deploy-ready)
deploy=true
prepare_for_deploy=true
shift
;;
--output)
@@ -270,10 +234,6 @@ main() {
verbose=true
shift
;;
--list-envs)
list_environments
exit 0
;;
--help|-h)
usage
exit 0
@@ -290,12 +250,6 @@ main() {
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"
@@ -303,6 +257,13 @@ main() {
exit 1
fi
# Set default mode if not specified
if [[ -z "$mode" ]]; then
mode="prod"
log_info "Mode not specified, defaulting to production"
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
@@ -338,39 +299,53 @@ main() {
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 2: Set version
# not required
# 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)
[[ "$set_version" == "true" ]] && build_opts+=(--version "$version")
"${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 ""
if [[ "$prepare_for_deploy" == "true" ]]; then
log_info "Preparing artifacts for deployment"
local artifact_opts=()
[[ "$clean" == "true" ]] && artifact_opts+=(--clean)
[[ -n "$output_dir" ]] && artifact_opts+=(--output "$output_dir")
[[ "$set_version" == "true" ]] && artifact_opts+=(--version "$version")
"${SCRIPT_DIR}/ci_prepare_artifacts.sh" "$environment" "${artifact_opts[@]}" || exit 1
echo ""
# Check if site folder exists and copy built files to avoid rebuilding the site
if [[ "$mode" == "dev" ]]; then
local site_dir="${PROJECT_ROOT}/generated/site/dev"
local artifacts_dir="${output_dir:-${PROJECT_ROOT}/generated/artifacts/firmware_build}"
if [[ -d "$site_dir" ]]; then
log_info "Site folder exists, copying built firmware files to site/dev..."
# Copy firmware files for the current environment
for file in "${artifacts_dir}/${environment}"-*.bin "${artifacts_dir}/${environment}"-*.tgz; do
if [[ -f "$file" ]]; then
cp -v "$file" "$site_dir/" || log_warn "Failed to copy $(basename "$file")"
fi
done
log_info "✓ Firmware files copied to site/dev (no site rebuild needed)"
else
log_warn "Site folder not found at: $site_dir"
log_info "Run 'ci.sh site ' to generate it"
fi
fi
fi
# Print summary
print_summary "$environment" "$version" "$start_time"
+29 -16
View File
@@ -6,8 +6,10 @@
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
# Load shared configuration (colors, logging functions, paths)
if [[ -f "${SCRIPT_DIR}/ci_00_config.sh" ]]; then
@@ -41,18 +43,7 @@ validate_environment() {
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() {
@@ -78,6 +69,18 @@ run_build() {
local env="$1"
local clean="${2:-false}"
local verbose="${3:-false}"
local enable_dev_ota="${4:-false}"
local version="${5:-edge}"
if [[ "$enable_dev_ota" == "true" ]]; then
log_info "Development OTA enabled"
export PLATFORMIO_BUILD_FLAGS='"-DDEVELOPMENTOTA=true"'
fi
if [[ -n "$version" ]]; then
log_info "Setting firmware version to: $version"
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DOMG_VERSION=\\\"${version}\\\""
fi
log_build "Starting build for environment: $env"
@@ -95,6 +98,9 @@ run_build() {
local start_time
start_time=$(date +%s)
log_info "PlatformIO Build Flags: $PLATFORMIO_BUILD_FLAGS"
log_info "Executing: $build_cmd"
if eval "$build_cmd"; then
local end_time
end_time=$(date +%s)
@@ -184,6 +190,7 @@ Options:
--clean Clean build artifacts before building
--verbose Enable verbose build output
--no-verify Skip artifact verification
--version <ver> Set firmware version (default: edge)
--help Show this help message
Examples:
@@ -201,10 +208,15 @@ main() {
local clean_build_flag=false
local verbose=false
local verify=true
local version="edge"
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--version)
version="$2"
shift 2
;;
--dev-ota)
enable_dev_ota=true
shift
@@ -254,7 +266,8 @@ main() {
validate_environment "$environment" || exit 1
# Setup build environment
setup_build_env "$enable_dev_ota"
export PYTHONIOENCODING=utf-8
export PYTHONUTF8=1
# Clean if requested
if [[ "$clean_build_flag" == "true" ]]; then
@@ -262,7 +275,7 @@ main() {
fi
# Run build
run_build "$environment" "$clean_build_flag" "$verbose" || exit 1
run_build "$environment" "$clean_build_flag" "$verbose" "$enable_dev_ota" "$version" || exit 1
# Verify artifacts
if [[ "$verify" == "true" ]]; then
+125
View File
@@ -0,0 +1,125 @@
#!/bin/bash
# Lists all available PlatformIO environments for OpenMQTTGateway
# Usage: ./scripts/ci_list.sh [--full]
set -euo pipefail
# Constants
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
# 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
## use .github/workflows/environments.json to list environments
list_environments_from_Json() {
local json_file="${PROJECT_ROOT}/.github/workflows/environments.json"
if [[ ! -f "$json_file" ]]; then
log_error "JSON file not found: $json_file"
return 1
fi
if ! command -v jq >/dev/null 2>&1; then
log_error "jq is required to read $json_file"
return 1
fi
log_info "Available PlatformIO environments from JSON:"
echo ""
local envs=()
while IFS= read -r env; do
# Skip test environments
if [[ ! "$env" =~ -test$ && ! "$env" =~ -all- ]]; then
envs+=("$env")
fi
done < <(jq -r '.environments.all[]? // empty' "$json_file")
# 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 JSON file"
fi
echo ""
log_info "Total: $(printf '%s\n' "${envs[@]}" | sort -u | wc -l) 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]}"
envs+=("$env_name")
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"
}
# Main execution
usage() {
cat <<EOF
Usage: ${0##*/} [--full]
List available PlatformIO environments for OpenMQTTGateway.
Options:
--full Parse platformio.ini and environments.ini for an exhaustive list
(slower, includes all defined envs except *-test / *-all-)
(default) Use .github/workflows/environments.json (fast, curated list)
Examples:
${0##*/} # quick, curated list
${0##*/} --full # exhaustive list from config files
EOF
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
USE_FULL=false
while [[ $# -gt 0 ]]; do
case "$1" in
--full)
USE_FULL=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
log_warn "Unknown option: $1"
usage
exit 1
;;
esac
done
if [[ "$USE_FULL" == true ]]; then
list_environments
else
list_environments_from_Json
fi
fi
+108 -134
View File
@@ -1,4 +1,5 @@
#!/bin/bash
# shellcheck disable=SC2015
# Prepares firmware artifacts for upload or deployment
# Used by: CI/CD pipelines for artifact packaging
# Usage: ./prepare_artifacts.sh <environment> [OPTIONS]
@@ -6,8 +7,10 @@
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
# Load shared configuration (colors, logging functions, paths)
if [[ -f "${SCRIPT_DIR}/ci_00_config.sh" ]]; then
@@ -22,17 +25,22 @@ BUILD_DIR="${PROJECT_ROOT}/${BUILD_DIR}"
DEFAULT_OUTPUT_DIR="${PROJECT_ROOT}/${ARTIFACTS_DIR}"
# Function to create output directory
create_output_dir() {
prepare_output_dir() {
local output_dir="$1"
local clean_flag="${2:-false}"
if [[ -d "$output_dir" ]]; then
log_warn "Output directory already exists: $output_dir"
log_info "Cleaning existing artifacts..."
rm -rf "$output_dir"
if [[ "$clean_flag" == "true" ]]; then
log_warn "Cleaning and recreating output directory: $output_dir"
rm -rf "$output_dir"
mkdir -p "$output_dir"
else
log_warn "Output directory already exists and will be reused: $output_dir"
fi
else
mkdir -p "$output_dir"
log_info "Created output directory: $output_dir"
fi
mkdir -p "$output_dir"
log_info "Created output directory: $output_dir"
}
# Function to copy artifact with optional renaming
@@ -58,42 +66,14 @@ copy_artifact() {
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() {
prepare_artifacts() {
local env="$1"
local output_dir="$2"
local env_dir="${BUILD_DIR}/${env}"
log_info "Preparing DEPLOYMENT artifacts for: $env"
log_info "Preparing firmware directory for: $env"
local copied=0
# Copy and rename firmware.bin
@@ -118,54 +98,57 @@ prepare_deployment_artifacts() {
log_info "Copied ${copied} artifact(s) in deployment mode"
}
# Function to create manifest file
create_manifest() {
prepare_libraries() {
local env="$1"
local output_dir="$2"
local manifest="${output_dir}/manifest.txt"
local env_dir="${BUILD_DIR}/${env}"
# Process libraries: create temp copy with renamed folders, zip, preserve originals
log_info "Processing libraries for environment: $env"
TEMP_LIBDEPS=$(mktemp -p "$output_dir" -d) || { echo "Failed to create temp directory"; return 1; }
cp -r .pio/libdeps/"$env" "$TEMP_LIBDEPS/" || { log_error "Failed to copy libdeps for $env"; return 1; }
(
cd "$TEMP_LIBDEPS"
log_step "Replace space by _ in folder names (temp copy only)"
find . -type d -name "* *" | while read -r FNAME; do
mv "$FNAME" "${FNAME// /_}"
done
log_info "Creating artifact manifest..."
log_step "Zipping libraries per board"
for i in */; do
tar -czf "${i%/}-libraries.tgz" "$i" > /dev/null
done
{
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"
mv ./*.tgz "${output_dir}"
)
rm -rf "$TEMP_LIBDEPS"
log_info "✓ Created library archives in: $output_dir"
}
# Function to compress artifacts (optional)
compress_artifacts() {
prepare_sources() {
local output_dir="$1"
local archive_name="$2"
log_info "Compressing artifacts..."
log_info "Preparing source code archive"
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)"
# Create and move sources tar.gz (newly generated, safe to move)
if tar -czf "${output_dir}/OpenMQTTGateway_sources.tgz" main LICENSE.txt > /dev/null; then
log_info "✓ Created source archive: OpenMQTTGateway_sources.tgz"
else
log_error "Failed to create archive"
log_error "Failed to create source archive"
return 1
fi
}
# Function to list artifacts
list_artifacts() {
local output_dir="$1"
@@ -194,17 +177,14 @@ usage() {
cat << EOF
Usage: $0 <environment> [OPTIONS]
Prepare firmware artifacts for upload or deployment.
Prepare artifacts for upload or deployment.
Arguments:
environment PlatformIO environment name
environment PlatformIO environment name, if omitted will be created source archive only.
Options:
--deploy Prepare for deployment (rename with environment prefix)
--standard Prepare standard artifacts (no renaming) [default]
--clean Clean existing output directory before preparing artifacts
--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:
@@ -218,32 +198,27 @@ EOF
# Main execution
main() {
local environment=""
local mode="standard"
local output_dir="$DEFAULT_OUTPUT_DIR"
local create_manifest_flag=false
local compress_flag=false
local clean_flag=false
#local version="" ## WILL BE USED WHEN THE VERSION ITSELF AFFECTS THE ARTIFACTS NAMING
# 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
-v|--version)
if [[ -z "${2:-}" ]]; then
log_error "-v|--version requires a version string"
return 1
fi
#version="$2"
shift 2
;;
--compress)
compress_flag=true
--clean)
clean_flag=true
shift
;;
--help|-h)
@@ -262,48 +237,47 @@ main() {
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"
# TODO FOR NEXT STEP MULTI RELEASE: TAG, RC, edge
#if [[ -n "$version" ]]; then
# # Sanitize version string for directory name
# safe_version=$(echo "$version" | sed 's/[^a-zA-Z0-9._-]/_/g')
# output_dir="${output_dir}/${safe_version}"
#fi
# Validate inputs
if [[ -z "$environment" ]]; then
log_info "No environment specified, only preparing source archive"
# Create output directory
prepare_output_dir "$output_dir" "$clean_flag"
# Prepare source code archive
prepare_sources "$output_dir" || exit 1
else
# 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
;;
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
#normalize output directory path
#output_dir="${output_dir}/firmware-${environment}"
output_dir="${output_dir}/firmware_build"
# Create output directory
prepare_output_dir "$output_dir" "$clean_flag"
# Prepare artifacts based on mode
prepare_artifacts "$environment" "$output_dir" || exit 1
# Prepare libraries
prepare_libraries "$environment" "$output_dir" || exit 1
fi
# Show summary
+70 -5
View File
@@ -6,8 +6,10 @@
set -euo pipefail
# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
# Load shared configuration
if [[ -f "${SCRIPT_DIR}/ci_00_config.sh" ]]; then
@@ -18,7 +20,6 @@ else
fi
# Default values
CHECK_MODE=true
FIX_MODE=false
FORMAT_ONLY=false
SOURCE_DIR="main"
@@ -202,6 +203,63 @@ fix_formatting() {
fi
}
# Function to run shellcheck on scripts
run_shellcheck() {
log_info "Checking shell scripts with ShellCheck..."
# Check if shellcheck is installed
if ! command -v shellcheck >/dev/null 2>&1; then
log_warn "ShellCheck not found, skipping shell script checks"
log_info "To install ShellCheck:"
log_info " Ubuntu/Debian: sudo apt-get install shellcheck"
log_info " macOS: brew install shellcheck"
return 0 # Don't fail, just skip
fi
log_info "✓ ShellCheck found: $(shellcheck --version | head -n2 | tail -n1)"
# Find all .sh files in scripts directory
local scripts_dir="${PROJECT_ROOT}/scripts"
if [[ ! -d "$scripts_dir" ]]; then
log_warn "Scripts directory not found: $scripts_dir"
return 0
fi
local shell_files
shell_files=$(find "$scripts_dir" -type f -name "*.sh" 2>/dev/null)
if [[ -z "$shell_files" ]]; then
log_warn "No shell scripts found in $scripts_dir"
return 0
fi
local file_count
file_count=$(echo "$shell_files" | wc -l)
log_info "Found $file_count shell script(s) to check"
# Run shellcheck on all files at once to allow cross-file analysis
log_info "Running ShellCheck on scripts directory..."
local shellcheck_output
# shellcheck disable=SC2086
shellcheck_output=$(shellcheck -f gcc $shell_files 2>&1)
local shellcheck_result=$?
if [[ $shellcheck_result -ne 0 ]]; then
echo ""
log_warn "⚠ ShellCheck found issues:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "$shellcheck_output"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
log_error "Please fix the ShellCheck warnings/errors"
return 1
fi
log_success "✓ All $file_count shell script(s) passed ShellCheck"
return 0
}
# Function to run all QA checks
run_all_checks() {
log_info "Running all quality assurance checks..."
@@ -215,6 +273,13 @@ run_all_checks() {
fi
echo ""
# ShellCheck
log_info "═══ Shell Scripts Check ═══"
if ! run_shellcheck; then
all_passed=false
fi
echo ""
# Future: Add more checks here
# - cppcheck static analysis
# - code complexity metrics
@@ -237,6 +302,7 @@ run_format_check() {
local clang_format_cmd
clang_format_cmd=$(check_clang_format "$CLANG_FORMAT_VERSION")
# shellcheck disable=SC2181
if [[ $? -ne 0 ]] || [[ -z "$clang_format_cmd" ]]; then
log_error "clang-format not found"
log_error "Please install clang-format:"
@@ -258,6 +324,7 @@ run_format_check() {
local files
files=$(find_files "$SOURCE_DIR" "$EXTENSIONS")
# shellcheck disable=SC2181
if [[ $? -ne 0 ]] || [[ -z "$files" ]]; then
log_error "Source directory not found: ${PROJECT_ROOT}/${SOURCE_DIR}"
return 1
@@ -317,13 +384,11 @@ 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)
+428
View File
@@ -0,0 +1,428 @@
#!/bin/bash
# OpenMQTTGateway Security Scan Script
# Runs Trivy vulnerability scanner and generates reports
# Based on task-security-scan.yml workflow
# Usage: ./scripts/ci_security.sh [OPTIONS]
set -euo pipefail
# Constants
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
# 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
# Default values
SCAN_TYPE="fs"
SEVERITY="HIGH,CRITICAL"
EXIT_CODE_ON_VULN="0"
SCAN_PATH="."
EXCLUDE_PATHS=""
GENERATE_SBOM=false # Off by default
OUTPUT_DIR="${PROJECT_ROOT}/${REPORTS_DIR}"
# Show usage
usage() {
cat << EOF
Usage: $0 [OPTIONS]
Security vulnerability scanning using Trivy
Options:
--scan-type <type> Type of scan: fs (filesystem), config, or image
Default: fs
--severity <levels> Severity levels to report (comma-separated)
Options: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
Default: HIGH,CRITICAL
--scan-path <path> Path to scan (default: entire repository)
Default: .
--exit-code <code> Exit code when vulnerabilities found
0 = continue (don't fail)
1 = fail build
Default: 0
--exclude <paths> Paths to exclude from scan (comma-separated)
Example: node_modules,test,docs
Default: (none)
--generate-sbom Generate Software Bill of Materials (SBOM)
Creates CycloneDX and SPDX JSON formats
Default: off
--output-dir <dir> Directory to save reports
Default: ./generated/reports
-h, --help Show this help message
Examples:
# Scan filesystem for HIGH and CRITICAL issues
$0
# Scan config files and fail if vulnerabilities found
$0 --scan-type config --exit-code 1
# Scan specific path for all severity levels
$0 --scan-path ./main --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
# Scan with excluded paths
$0 --exclude node_modules,test,docs
# Scan with multiple options
$0 --scan-path ./main --exclude test --severity HIGH,CRITICAL --exit-code 1
# Scan with SBOM generation
$0 --generate-sbom
# Complete scan with SBOM and custom severity
$0 --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --generate-sbom
EOF
exit 0
}
# Function to check if Trivy is installed
check_trivy() {
if ! command -v trivy &> /dev/null; then
log_error "Trivy is not installed. Install it from: https://github.com/aquasecurity/trivy"
return 1
fi
log_success "Trivy found: $(trivy --version)"
}
# Function to check if jq is installed (for JSON parsing)
check_jq() {
if ! command -v jq &> /dev/null; then
log_warn "jq is not installed. Some report features will be limited."
return 1
fi
return 0
}
# Function to run Trivy scan with specified format
# Usage: run_trivy_scan <format> <filename> [exit_code]
run_trivy_scan() {
local format="$1"
local filename="$2"
local exit_code="${3:-0}" # Default to 0 for non-critical scans
local output_file="${OUTPUT_DIR}/${filename}"
log_info "Running Trivy $SCAN_TYPE scan ($format format)..."
mkdir -p "$OUTPUT_DIR"
# Build Trivy command with all enabled scanners
local trivy_cmd=(
trivy "$SCAN_TYPE" "$SCAN_PATH"
--format "$format"
--output "$output_file"
--severity "$SEVERITY"
--exit-code "$exit_code"
)
# Add exclude paths if provided
if [[ -n "$EXCLUDE_PATHS" ]]; then
IFS=',' read -r -a exclude_dirs <<< "$EXCLUDE_PATHS"
for skip_dir in "${exclude_dirs[@]}"; do
if [[ -n "$skip_dir" ]]; then
trivy_cmd+=(--skip-dirs "$skip_dir")
fi
done
fi
log_info "Executing Trivy ${SCAN_TYPE} scan"
if "${trivy_cmd[@]}" 2>&1 | tee -a "${OUTPUT_DIR}/trivy-scan.log"; then
log_success "${format^^} report generated: $output_file"
return 0
else
local rc=$?
if [[ $rc -eq 1 && "$exit_code" == "1" ]]; then
log_error "Vulnerabilities found (exit code: $rc)"
return $rc
fi
log_success "Scan completed with exit code: $rc"
return 0
fi
}
# Function to create summary report
create_summary_report() {
log_info "Creating security summary report..."
local summary_file="${OUTPUT_DIR}/security-summary.md"
{
echo "# 🔒 Security Scan Results"
echo ""
echo "**Scan Type**: $SCAN_TYPE"
echo "**Path Scanned**: $SCAN_PATH"
echo "**Severity Filter**: $SEVERITY"
echo "**Scan Date**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
echo ""
} > "$summary_file"
# Parse SARIF if it exists
local sarif_file="${OUTPUT_DIR}/trivy-results.sarif"
if [[ -f "$sarif_file" ]]; then
log_info "Parsing SARIF results..."
if check_jq; then
# Count vulnerabilities by severity
local vuln_count
vuln_count=$(jq '[.runs[].results[]] | length' "$sarif_file" 2>/dev/null || echo "0")
local critical high medium low
critical=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$sarif_file" 2>/dev/null || echo "0")
high=$(jq '[.runs[].results[] | select(.level == "warning")] | length' "$sarif_file" 2>/dev/null || echo "0")
medium=$(jq '[.runs[].results[] | select(.level == "note")] | length' "$sarif_file" 2>/dev/null || echo "0")
low=$(jq '[.runs[].results[] | select(.level == "none")] | length' "$sarif_file" 2>/dev/null || echo "0")
{
echo "## Vulnerability Summary"
echo ""
echo "**Total Vulnerabilities**: ${vuln_count}"
echo ""
echo "- 🔴 **Critical**: ${critical}"
echo "- 🟠 **High**: ${high}"
echo "- 🟡 **Medium**: ${medium}"
echo "- 🟢 **Low**: ${low}"
echo ""
} >> "$summary_file"
# List vulnerability details
if [[ "$vuln_count" -gt 0 ]]; then
{
echo "## Vulnerability Details"
echo ""
} >> "$summary_file"
jq -r '.runs[].results[] |
"### \(.level | ascii_upcase): \(.ruleId)\n" +
"**Location**: \(.locations[0].physicalLocation.artifactLocation.uri // "N/A")\n" +
"**Message**: \(.message.text)\n" +
(if .properties.CVE then "**CVE**: \(.properties.CVE)\n" else "" end) +
(if .properties.cvss then "**CVSS Score**: \(.properties.cvss)\n" else "" end) +
""' "$sarif_file" >> "$summary_file" 2>/dev/null || \
echo "Could not parse vulnerability details" >> "$summary_file"
fi
else
echo "⚠️ jq not available for detailed SARIF parsing" >> "$summary_file"
fi
else
echo "⚠️ SARIF file not found" >> "$summary_file"
fi
# Add available report formats
{
echo "## Report Formats Available"
echo ""
if [[ -f "${OUTPUT_DIR}/trivy-results.sarif" ]]; then
echo "- ✅ \`trivy-results.sarif\` - SARIF format (for GitHub/IDE integration)"
fi
if [[ -f "${OUTPUT_DIR}/trivy-report.json" ]]; then
echo "- ✅ \`trivy-report.json\` - JSON format (for automation)"
fi
if [[ -f "${OUTPUT_DIR}/trivy-report.txt" ]]; then
echo "- ✅ \`trivy-report.txt\` - Human-readable table"
fi
echo ""
} >> "$summary_file"
# Add footer
{
echo "---"
echo ""
echo "Generated by OpenMQTTGateway CI/CD Security Scan"
echo "For more information, see: https://github.com/aquasecurity/trivy"
} >> "$summary_file"
log_success "Summary report generated: $summary_file"
}
# Function to check for critical vulnerabilities and fail if needed
check_critical_vulnerabilities() {
if [[ "$EXIT_CODE_ON_VULN" != "1" ]]; then
return 0
fi
local sarif_file="${OUTPUT_DIR}/trivy-results.sarif"
if [[ ! -f "$sarif_file" ]]; then
log_warn "SARIF file not found, skipping critical check"
return 0
fi
if check_jq; then
local critical
critical=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$sarif_file" 2>/dev/null || echo "0")
if [[ "$critical" -gt 0 ]]; then
log_error "❌ Found ${critical} critical vulnerabilities!"
log_error "Review the security reports in: $OUTPUT_DIR"
return 1
fi
fi
return 0
}
# Main execution
main() {
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--scan-type)
if [[ -z "${2:-}" ]]; then
log_error "--scan-type requires an argument"
return 1
fi
SCAN_TYPE="$2"
shift 2
;;
--severity)
if [[ -z "${2:-}" ]]; then
log_error "--severity requires an argument"
return 1
fi
SEVERITY="$2"
shift 2
;;
--scan-path)
if [[ -z "${2:-}" ]]; then
log_error "--scan-path requires an argument"
return 1
fi
SCAN_PATH="$2"
shift 2
;;
--exit-code)
if [[ -z "${2:-}" ]]; then
log_error "--exit-code requires an argument"
return 1
fi
EXIT_CODE_ON_VULN="$2"
shift 2
;;
--exclude)
if [[ -z "${2:-}" ]]; then
log_error "--exclude requires an argument"
return 1
fi
EXCLUDE_PATHS="$2"
shift 2
;;
--generate-sbom)
GENERATE_SBOM=true
shift
;;
--output-dir)
if [[ -z "${2:-}" ]]; then
log_error "--output-dir requires an argument"
return 1
fi
OUTPUT_DIR="$2"
shift 2
;;
-h|--help)
usage
;;
*)
log_error "Unknown option: $1"
echo ""
usage
;;
esac
done
# Pre-flight checks
log_info "Running security scan checks..."
if ! check_trivy; then
log_error "Required tool check failed"
return 1
fi
check_jq || true
echo ""
log_info "═══ Security Scan Configuration ═══"
echo " Scan Type: $SCAN_TYPE"
echo " Path: $SCAN_PATH"
echo " Severity: $SEVERITY"
echo " Exit on Vulnerabilities: $EXIT_CODE_ON_VULN"
echo " Excluded Paths: ${EXCLUDE_PATHS:-none}"
echo " Generate SBOM: ${GENERATE_SBOM:-false}"
echo " Output Directory: $OUTPUT_DIR"
echo ""
# Run scans
if ! run_trivy_scan "sarif" "trivy-results.sarif" "$EXIT_CODE_ON_VULN"; then
if [[ "$EXIT_CODE_ON_VULN" == "1" ]]; then
return 1
fi
fi
run_trivy_scan "json" "trivy-report.json" "0"
run_trivy_scan "table" "trivy-report.txt" "0"
# Create summary
create_summary_report
# Generate SBOM if requested
if [[ "$GENERATE_SBOM" == "true" ]]; then
log_info "Generating Software Bill of Materials (SBOM)..."
local sbom_dir="${OUTPUT_DIR}/sbom"
mkdir -p "$sbom_dir"
# Generate CycloneDX SBOM
log_info "Generating CycloneDX SBOM..."
if trivy "$SCAN_TYPE" "$SCAN_PATH" \
--format cyclonedx \
--output "$sbom_dir/sbom-cyclonedx.json" \
--exit-code 0 2>&1 | grep -i "generated\|error" || true; then
if [[ -f "$sbom_dir/sbom-cyclonedx.json" ]]; then
log_success "CycloneDX SBOM generated: $sbom_dir/sbom-cyclonedx.json"
fi
fi
# Generate SPDX SBOM
log_info "Generating SPDX SBOM..."
if trivy "$SCAN_TYPE" "$SCAN_PATH" \
--format spdx-json \
--output "$sbom_dir/sbom-spdx.json" \
--exit-code 0 2>&1 | grep -i "generated\|error" || true; then
if [[ -f "$sbom_dir/sbom-spdx.json" ]]; then
log_success "SPDX SBOM generated: $sbom_dir/sbom-spdx.json"
fi
fi
if [[ -f "$sbom_dir/sbom-cyclonedx.json" ]] || [[ -f "$sbom_dir/sbom-spdx.json" ]]; then
log_success "SBOM generation completed"
echo ""
fi
fi
# Check for critical vulnerabilities
if ! check_critical_vulnerabilities; then
return 1
fi
echo ""
log_success "Security scan completed successfully!"
log_info "Reports saved to: $OUTPUT_DIR"
return 0
}
# Execute main function
main "$@"
-197
View File
@@ -1,197 +0,0 @@
#!/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
+161 -310
View File
@@ -1,16 +1,19 @@
#!/bin/bash
# CI/CD Site/Documentation Builder
# Builds and deploys VuePress documentation with version management
# Usage: ./scripts/ci_site.sh [OPTIONS]
# This script builds and deploys VuePress documentation with version management.
# Usage: ./scripts/ci_site.sh [OPTIONS] - Specify options for the script execution.
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"
# Resolve the folder containing this script so relative paths work
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
DOCS_DIR="${PROJECT_ROOT}/docs"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
readonly DOCS_DIR
# Load shared configuration
if [[ -f "${SCRIPT_DIR}/ci_00_config.sh" ]]; then
@@ -20,29 +23,23 @@ else
exit 1
fi
# Final output directory for site
# Final output directory for the 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/"
MODE="prod" # Set the default mode to production
CURL_INSECURE=false # Allow curl to skip TLS verification (use only when needed)
CLEAN=false # Clean generated/site before build
LEGACY_OPENSSL=false # Use --openssl-legacy-provider for older Node.js versions
# 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")
missing_tools+=("node") # Node.js is required
else
local node_version
node_version=$(node --version)
@@ -50,27 +47,19 @@ check_requirements() {
fi
if ! command -v npm >/dev/null 2>&1; then
missing_tools+=("npm")
missing_tools+=("npm") # npm is required
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")
if ! command -v openssl >/dev/null 2>&1; then
missing_tools+=("openssl") # OpenSSL is required
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"
local openssl_version
openssl_version=$(openssl version | grep -oP '\d+\.\d+\.\d+' || echo "unknown")
log_info "✓ OpenSSL ${openssl_version} found"
fi
if [[ ${#missing_tools[@]} -gt 0 ]]; then
@@ -86,21 +75,6 @@ check_requirements() {
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}"
@@ -118,118 +92,49 @@ download_common_config() {
local config_url="https://www.theengs.io/commonConfig.js"
local config_dest="${DOCS_DIR}/.vuepress/public/commonConfig.js"
local curl_opts="-sSf"
# Optionally disable TLS verification if explicitly requested
if [[ "${CURL_INSECURE}" == "true" ]]; then
curl_opts+="k"
log_warn "curl is running with --insecure; TLS verification is disabled"
fi
mkdir -p "$(dirname "$config_dest")"
if curl -sSf -o "$config_dest" "$config_url"; then
if curl ${curl_opts} -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() {
create_configuration_files() {
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
}
local version="$2"
local mode="$3"
local dest_line=""
# 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..."
if [[ "$mode" == "dev" ]]; then
dest_line=" \"dest\": \"generated/site/dev\","
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
# download common config
download_common_config
## Create a meta.json file on config folder
local meta_file="${DOCS_DIR}/.vuepress/meta.json"
cat > "$meta_file" <<EOF
{
"title": "Theengs OpenMQTTGateway",
"version": "${version}",
"url_prefix": "${url_prefix}",
${dest_line}
"mode": "${mode}"
}
EOF
log_info "✓ Generated meta.json at ${meta_file}"
}
# Function to build documentation
@@ -238,45 +143,44 @@ build_docs() {
cd "${PROJECT_ROOT}"
# Set Node options for compatibility with newer Node.js versions
export NODE_OPTIONS="--openssl-legacy-provider"
# Set Node options for compatibility with older Node.js versions (if requested)
if [[ "$LEGACY_OPENSSL" == true ]]; then
log_info "Using --openssl-legacy-provider for older Node.js compatibility"
export NODE_OPTIONS="--openssl-legacy-provider"
fi
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
log_info "✓ Documentation built successfully"
}
# Function to preview documentation
preview_docs() {
log_info "Starting documentation preview..."
log_info "Starting local preview server..."
if [[ ! -d "$SITE_OUTPUT_DIR" ]]; then
log_error "Build output not found. Run build first."
cd "${PROJECT_ROOT}"
npm run site:preview || {
log_error "Failed to start preview server"
return 1
}
log_info "Preview server running at https://localhost:8443"
log_info "Press Ctrl+C to stop the preview server"
}
# Function to clean generated site folder
clean_build() {
local site_folder="${PROJECT_ROOT}/generated/site"
if [[ -d "$site_folder" ]]; then
log_info "Cleaning ${site_folder}..."
rm -rf "$site_folder"
log_info "✓ Cleaned ${site_folder}"
else
log_info "Nothing to clean: ${site_folder} does not exist"
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
@@ -284,204 +188,148 @@ 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
Build and deploy OpenMQTTGateway documentation with VuePress.
Generates site structure and meta.json file.
OPTIONS:
--mode MODE
Build mode: 'prod' or 'dev' [default: prod]
- prod: Production documentation with release version
- dev: Development documentation with custom version tag
Build mode: 'prod' (production) or 'dev' (development) [default: dev]
--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
Base URL path for links (e.g., '/' for root, '/dev/' for dev) [default: /dev/]
--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
--version VERSION
Version string written to meta.json (e.g., '1.8.0', 'edge') [default: edge]
--preview
Start local HTTP server to preview built documentation
Server will run at http://localhost:8080
Press Ctrl+C to stop the preview server
Start local HTTPS preview server after build (https://localhost:8443)
--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
--clean
Remove generated/site folder before build
--no-webuploader
Skip WebUploader manifest generation
By default, generates manifest for web-based firmware updates
--insecure-curl
Allow curl to skip TLS verification (use only if cert errors block download)
--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/
--legacy-openssl
Use --openssl-legacy-provider for older Node.js versions (pre-17)
--help
Show this help message and exit
Show this message and exit
EXAMPLES:
# Production build
$0 --mode prod --url-prefix / --version 1.8.0
# Build production documentation with latest release version
$0 --mode prod
# Development build with preview
$0 --mode dev --url-prefix /dev/ --version edge --preview
# 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/
# Clean and rebuild
$0 --clean --mode prod --version 1.8.0
EOF
exit 0
}
# Main execution
main() {
local version="edge"
local mode="dev"
local url_prefix=""
local url_prefix_set=false
local do_preview=false
# Parse command line arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--mode)
MODE="$2"
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
;;
--url-prefix|--base-path)
URL_PREFIX="$2"
--url-prefix)
url_prefix="$2"
url_prefix_set=true
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"
--version)
version="$2"
shift 2
;;
--preview)
PREVIEW=true
do_preview=true
shift
;;
--no-webuploader)
GENERATE_WEBUPLOADER=false
--clean)
CLEAN=true
shift
;;
--webuploader-args)
WEBUPLOADER_ARGS="$2"
shift 2
;;
--run-pagespeed)
RUN_PAGESPEED=true
--insecure-curl)
CURL_INSECURE=true
shift
;;
--pagespeed-url)
PAGESPEED_URL="$2"
shift 2
--legacy-openssl)
LEGACY_OPENSSL=true
shift
;;
--help)
usage
exit 0
;;
*)
log_error "Unknown option: $1"
usage
exit 0
;;
esac
done
}
# Main execution
main() {
# Set url_prefix based on mode if not explicitly provided
if [[ "$url_prefix_set" == false ]]; then
if [[ "$mode" == "prod" ]]; then
url_prefix="/"
else
url_prefix="/dev/"
fi
fi
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"
log_info "Mode: $mode"
log_info "URL prefix: $url_prefix"
# Clean if requested
if [[ "$CLEAN" == "true" ]]; then
clean_build
fi
# 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
# Create configuration files (meta.json)
create_configuration_files "$url_prefix" "$version" "$mode" || exit 1
# Build documentation
build_docs || exit 1
# Preview if requested
if [[ "$PREVIEW" == true ]]; then
if [[ "$do_preview" == true ]]; then
preview_docs
fi
@@ -501,5 +349,8 @@ main() {
echo "╚════════════════════════════════════════╝"
}
# Execute main function
main "$@"
# Run main if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
-116
View File
@@ -1,116 +0,0 @@
# Common templates and constants for web installer manifest generation
# Used by: scripts/gen_wu.py
import string
mf_temp32 = string.Template('''{
"name": "OpenMQTTGateway",
"new_install_prompt_erase": true,
"builds": [
{
"chipFamily": "ESP32",
"improv": false,
"parts": [
{ "path": "$cp$bl", "offset": 4096 },
{ "path": "$cp$part", "offset": 32768 },
{ "path": "$cp$boot", "offset": 57344 },
{ "path": "$cp$bin", "offset": 65536 }
]
}
]
}''')
mf_temp32c3 = string.Template('''{
"name": "OpenMQTTGateway",
"new_install_prompt_erase": true,
"builds": [
{
"chipFamily": "ESP32-C3",
"improv": false,
"parts": [
{ "path": "$cp$bl", "offset": 0 },
{ "path": "$cp$part", "offset": 32768 },
{ "path": "$cp$boot", "offset": 57344 },
{ "path": "$cp$bin", "offset": 65536 }
]
}
]
}''')
mf_temp32s3 = string.Template('''{
"name": "OpenMQTTGateway",
"new_install_prompt_erase": true,
"builds": [
{
"chipFamily": "ESP32-S3",
"improv": false,
"parts": [
{ "path": "$cp$bl", "offset": 0 },
{ "path": "$cp$part", "offset": 32768 },
{ "path": "$cp$boot", "offset": 57344 },
{ "path": "$cp$bin", "offset": 65536 }
]
}
]
}''')
mf_temp8266 = string.Template('''{
"name": "OpenMQTTGateway",
"new_install_prompt_erase": true,
"builds": [
{
"chipFamily": "ESP8266",
"parts": [{ "path": "$cp$bin", "offset": 0 }]
}
]
}''')
wu_temp_opt = string.Template('''
<option
value="$mff"
>
$mfn
</option>
''')
wu_temp_p1 = '''<template>
<div align="center">
<select>
<optgroup label="ESP32">'''
wu_temp_p2 = '''
</optgroup>
<optgroup label="ESP32C3">'''
wu_temp_p3 = '''
</optgroup>
<optgroup label="ESP32S3">'''
wu_temp_p4 = '''
</optgroup>
<optgroup label="ESP8266">'''
wu_temp_end = '''
</optgroup>
</select><br><br>
<esp-web-install-button erase-first></esp-web-install-button>
</div>
</template>
<script>
export default {
mounted () {
const espWebInstallButton = document.querySelector("esp-web-install-button");
espWebInstallButton.addEventListener("state-changed", (ev) => { console.log(ev.detail) });
const selectFW = document.querySelector("select");
espWebInstallButton.manifest = selectFW.value;
selectFW.addEventListener("change", () => {
espWebInstallButton.manifest = selectFW.value;
});
}
}
</script>'''
manif_path = 'docs/.vuepress/public/firmware_build/'
vue_path = 'docs/.vuepress/components/'
cors_proxy = '' # 'https://cors.bridged.cc/'
esp32_boot = 'https://github.com/espressif/arduino-esp32/raw/2.0.7/tools/partitions/boot_app0.bin'
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const path = require('path');
function ensureSSLCerts() {
const sslDir = path.join(process.cwd(), '.ssl');
const keyFile = path.join(sslDir, 'key.pem');
const certFile = path.join(sslDir, 'cert.pem');
if (fs.existsSync(keyFile) && fs.existsSync(certFile)) {
return;
}
const { execSync } = require('child_process');
fs.mkdirSync(sslDir, { recursive: true });
try {
execSync(`openssl req -new -x509 -keyout "${keyFile}" -out "${certFile}" -days 365 -nodes -subj "/C=US/ST=State/L=City/O=OpenMQTTGateway/CN=localhost" 2>/dev/null`, {
stdio: 'inherit'
});
console.log('✓ SSL certificate generated');
} catch (err) {
console.error('Failed to generate SSL certificate. Ensure openssl is installed.');
process.exit(1);
}
}
ensureSSLCerts();
-149
View File
@@ -1,149 +0,0 @@
# Creates web installer manifests for ESP Web Tools firmware installation
# Used by: .github/workflows/task-docs.yml
import os
import requests
import json
import argparse
import shutil
from common_wu import mf_temp32, mf_temp32c3, mf_temp8266, wu_temp_opt, wu_temp_p1, wu_temp_p2, wu_temp_p3, wu_temp_p4, wu_temp_end, vue_path, manif_path, cors_proxy, esp32_boot, mf_temp32s3
parser = argparse.ArgumentParser()
parser.add_argument('--dev', action='store_true')
parser.add_argument('repo', nargs='?', default='1technophile/OpenMQTTGateway')
args = parser.parse_args()
repo = args.repo
dev = args.dev
bin_path = 'toDeploy/'
manif_folder = "/firmware_build/"
if not os.path.exists(manif_path):
os.makedirs(manif_path)
if dev:
print('Generate Web Upload in dev mode')
manif_folder = "/dev" + manif_folder
# copy OTA latest version definition
shutil.copy("scripts/latest_version_dev.json",
manif_path + "latest_version_dev.json")
# copy the binaries frombin_path to manif_path
for name in os.listdir(bin_path):
if '.bin' in name:
shutil.copyfile(bin_path + name, (manif_path + name))
else:
print('Generate Web Upload in release mode')
# copy OTA latest version definition
shutil.copy("scripts/latest_version.json", manif_path + "latest_version.json")
release = requests.get('https://api.github.com/repos/' +
repo + '/releases/latest')
rel_data = json.loads(release.text)
if 'assets' in rel_data:
assets = rel_data['assets']
# Download assets into manif_path
for item in range(len(assets)):
name = assets[item]['name']
if 'firmware.bin' in name:
fw_bin = requests.get(assets[item]['browser_download_url'])
filename = assets[item]['browser_download_url'].split('/')[-1]
with open(manif_path + filename, 'wb') as output_file:
output_file.write(fw_bin.content)
print('Downloaded: ' + filename)
if 'partitions.bin' in name:
part_bin = requests.get(assets[item]['browser_download_url'])
filename = assets[item]['browser_download_url'].split('/')[-1]
with open(manif_path + filename, 'wb') as output_file:
output_file.write(part_bin.content)
print('Downloaded: ' + filename)
if 'bootloader.bin' in name:
bl_bin = requests.get(assets[item]['browser_download_url'])
filename = assets[item]['browser_download_url'].split('/')[-1]
with open(manif_path + filename, 'wb') as output_file:
output_file.write(bl_bin.content)
print('Downloaded: ' + filename)
else:
print('Assets not found')
os._exit(1)
if not os.path.exists(vue_path):
os.makedirs(vue_path)
boot_bin = requests.get(esp32_boot)
filename = esp32_boot.split('/')[-1]
with open(manif_path + filename, 'wb') as output_file:
output_file.write(boot_bin.content)
wu_file = open(vue_path + 'web-uploader.vue', 'w')
wu_file.write(wu_temp_p1)
for name in sorted(os.listdir(manif_path)):
if 'firmware.bin' in name and ('esp32c3' not in name ) and ('esp32s3' not in name ) and ('esp32' in name or 'ttgo' in name or 'heltec' in name or 'thingpulse' in name or 'theengs' in name or 'lilygo' in name or 'shelly' in name or 'tinypico' in name):
fw = name.split('-firmware')[0]
man_file = fw + '.manifest.json'
fwp_name = name.split('-firmware')[0] + '-partitions.bin'
fwb_name = name.split('-firmware')[0] + '-bootloader.bin'
mani_str = mf_temp32.substitute({'cp': cors_proxy, 'part': manif_folder + fwp_name.split('/')[-1], 'bin': manif_folder + name, 'bl': manif_folder + fwb_name, 'boot': manif_folder + esp32_boot.split('/')[-1]})
with open(manif_path + man_file, 'w') as nf:
nf.write(mani_str)
wu_file.write(wu_temp_opt.substitute(
{'mff': manif_folder + man_file, 'mfn': fw}))
print('Created: ' + man_file)
wu_file.write(wu_temp_p2)
for name in sorted(os.listdir(manif_path)):
if 'firmware.bin' in name and ('esp32c3' in name ):
fw = name.split('-firmware')[0]
man_file = fw + '.manifest.json'
fwp_name = name.split('-firmware')[0] + '-partitions.bin'
fwb_name = name.split('-firmware')[0] + '-bootloader.bin'
mani_str = mf_temp32c3.substitute({'cp': cors_proxy, 'part': manif_folder + fwp_name, 'bin': manif_folder + name, 'bl': manif_folder + fwb_name, 'boot': manif_folder + esp32_boot.split('/')[-1]})
with open(manif_path + man_file, 'w') as nf:
nf.write(mani_str)
wu_file.write(wu_temp_opt.substitute(
{'mff': manif_folder + man_file, 'mfn': fw}))
print('Created: ' + man_file)
wu_file.write(wu_temp_p3)
for name in sorted(os.listdir(manif_path)):
if 'firmware.bin' in name and ('esp32s3' in name ):
fw = name.split('-firmware')[0]
man_file = fw + '.manifest.json'
fwp_name = name.split('-firmware')[0] + '-partitions.bin'
fwb_name = name.split('-firmware')[0] + '-bootloader.bin'
mani_str = mf_temp32s3.substitute({'cp': cors_proxy, 'part': manif_folder + fwp_name, 'bin': manif_folder + name, 'bl': manif_folder + fwb_name, 'boot': manif_folder + esp32_boot.split('/')[-1]})
with open(manif_path + man_file, 'w') as nf:
nf.write(mani_str)
wu_file.write(wu_temp_opt.substitute(
{'mff': manif_folder + man_file, 'mfn': fw}))
print('Created: ' + man_file)
wu_file.write(wu_temp_p4)
for name in sorted(os.listdir(manif_path)):
if 'firmware.bin' in name and ('nodemcu' in name or 'sonoff' in name or 'rf-wifi-gateway' in name or 'manual-wifi-test' in name or 'rfbridge' in name):
fw = name.split('-firmware')[0]
man_file = fw + '.manifest.json'
mani_str = mf_temp8266.substitute(
{'cp': cors_proxy, 'bin': manif_folder + name})
with open(manif_path + man_file, 'w') as nf:
nf.write(mani_str)
wu_file.write(
manif_folder + wu_temp_opt.substitute({'mff': manif_folder + man_file, 'mfn': fw}))
print('Created: ' + man_file)
wu_file.write(wu_temp_end)
wu_file.close()
-90
View File
@@ -1,90 +0,0 @@
# 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
import re
import configparser
conf = configparser.ConfigParser()
# Init the table with the columns
table_init = pd.DataFrame(columns=['Environment', 'uC', 'Hardware', 'Description', 'Modules', 'Platform',
'Partitions', 'Libraries', 'Options'])
table = table_init
# Parse platformio.ini to retrieve boards information
conf.read('environments.ini')
for each_section in conf.sections():
if ("env:" in each_section and "-test" not in each_section):
env = each_section.replace("env:", "")
uc = ""
board = ""
hardware = ""
description = ""
modules = ""
platform = ""
partitions = ""
libraries = ""
options = ""
for (k, v) in conf.items(each_section):
v = v.replace('{', '').replace('}', '').replace('$', '').replace(
"env:", '').replace('\'', '').replace("-D", "")
if (k == "board"):
uc = v
if (k == "platform"):
platform = v
if (k == "board_build.partitions"):
partitions = v
if (k == "lib_deps"):
libraries = v
libraries = libraries.replace(
"\ncom-esp.lib_deps\n", "").replace(
"\ncom-arduino.lib_deps\n", "").replace("libraries.", "")
if (k == "build_flags"):
options = v
for o in options.split('\n'):
if ("gateway" in o or "sensor" in o or "actuator" in o):
if (modules != ""):
modules = modules + "\n"
modules = modules + o[1:o.rfind("=\"")]
options = options.replace(
"com-esp.build_flags\n", "")
if (k == "custom_description"):
description = v
if (k == "custom_hardware"):
hardware = v
table.loc[len(table.index)] = [env, uc, hardware, description, modules, platform,
partitions, libraries, options]
# Sort rows per Environment name
table.sort_values(by=['Environment'], inplace=True,
key=lambda col: col.str.lower())
# Produce individual file
for ind in table.index:
table_extract = table.iloc[ind]
print(table_extract)
file = open("docs/prerequisites/boards/" +
table.iloc[ind]["Environment"] + ".md", 'w')
table_extract = table_extract.rename_axis("Board index")
table_md = table_extract.to_markdown()
n = file.write(table_md)
file.close()
# Produce list file
# Add link to the file from the environment and replace /n with ,
for ind in table.index:
table['Environment'][ind] = "[" + table['Environment'][ind] + \
"](../prerequisites/boards/" + table['Environment'][ind] + ")"
table = table.replace("\n", ", ", regex=True)
table = table.drop(["Partitions", "Hardware", "Platform", "Options","Modules"], axis=1)
table = table.reset_index(drop=True)
print(table)
# Convert to Markdown and save per Model_Id
table_md = table.to_markdown()
file = open("docs/prerequisites/board.md", 'a')
n = file.write("# Supported\n" + table_md)
file = open("docs/upload/web-install.md", 'a')
n = file.write(table_md)
file.close()
-5
View File
@@ -1,5 +0,0 @@
{
"latest_version": "version_tag",
"title": "OpenMQTTGateway",
"release_url": "https://github.com/1technophile/OpenMQTTGateway/releases/tag/version_tag"
}
-4
View File
@@ -1,4 +0,0 @@
{
"latest_version": "version_tag",
"title": "OpenMQTTGateway Nightly (TEST ONLY)"
}
-28
View File
@@ -1,28 +0,0 @@
#!/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
mkdir toDeploy
rename 's/.pio-build-//' .*.bin
(
cd .pio/libdeps
echo "replace space by _ in folder names"
find . -type d -name "* *" | while read FNAME; do mv "$FNAME" "${FNAME// /_}"; done
echo "zipping libraries per board"
for i in */
do
zip -r "${i%/}-libraries.zip" "$i"
done
ls -lA
mv *.zip ../../toDeploy
)
# remove binaries for *-all*, *-test* env and only zip containing *-test*
rm -f *-all*.bin *-test*.bin *-test*.zip
echo "zipping code and licence"
zip -r OpenMQTTGateway_sources.zip main LICENSE.txt
mv *.zip toDeploy
mv *.bin toDeploy
ls -lA toDeploy
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env node
'use strict';
// Minimal HTTPS static server for generated/site only.
const fs = require('fs');
const path = require('path');
const https = require('https');
const mime = require('mime-types');
const repoRoot = path.resolve(__dirname, '..');
const siteRoot = path.join(repoRoot, 'generated', 'site');
const port = parseInt(process.argv[2] || 8443, 10);
const keyFile = process.argv[3] || path.join(repoRoot, '.ssl', 'key.pem');
const certFile = process.argv[4] || path.join(repoRoot, '.ssl', 'cert.pem');
if (!fs.existsSync(siteRoot)) {
console.error(`Error: folder not found: ${siteRoot}`);
process.exit(1);
}
if (!fs.existsSync(keyFile) || !fs.existsSync(certFile)) {
console.error(`Error: SSL certificates not found at ${keyFile} or ${certFile}`);
console.error('Use scripts/ensure_ssl_certs.js to generate them.');
process.exit(1);
}
const server = https.createServer({
key: fs.readFileSync(keyFile),
cert: fs.readFileSync(certFile)
}, (req, res) => {
const reqPath = decodeURIComponent(req.url.split('?')[0]);
const safePath = reqPath.endsWith('/') ? `${reqPath}index.html` : reqPath;
const filePath = path.join(siteRoot, safePath);
if (!filePath.startsWith(siteRoot)) {
res.writeHead(403);
return res.end('Forbidden');
}
fs.stat(filePath, (err, stats) => {
if (err || !stats.isFile()) {
res.writeHead(404);
return res.end('Not Found');
}
const type = mime.contentType(path.extname(filePath)) || 'application/octet-stream';
res.writeHead(200, { 'Content-Type': type });
fs.createReadStream(filePath).pipe(res);
});
});
server.listen(port, '0.0.0.0', () => {
console.log(`Serving generated/site over HTTPS at https://localhost:${port}/`);
console.log(`Serving generated/site over HTTPS at https://<host-ip>:${port}/`);
console.log(`Note: remember that if you are testing DEV site you should go on https://<host-ip>:${port}/dev/`);
});
process.on('SIGINT', () => {
server.close(() => process.exit());
});