From e9b94359dfe1566881830e4d8d7a0b43aaf15102 Mon Sep 17 00:00:00 2001 From: GiZano Date: Thu, 23 Apr 2026 23:27:29 +0200 Subject: [PATCH 1/3] ci: add IoT firmware CI pipeline with PlatformIO build validation - Add `iot-ci` job to `.github/workflows/quakeguard-ci.yml` running on `ubuntu-latest`. - Install PlatformIO Core via Python `pip` in the GitHub Action runner. - Create `esp32_config.env.example` with dummy values for CI testing. - Inject dummy `ENROLLMENT_TOKEN` into the build environment to satisfy the `#error` fail-fast compiler check. - Execute `pio run` to validate firmware compilation cleanly without attempting hardware flashing. - Verify pipeline correctly catches introduced C++ syntax errors. Closes #215, Closes #216, Closes #217, Closes #218, Closes #219, Closes #220, Closes #221 --- .github/workflows/quakeguard-ci.yml | 44 ++++++++++++++++++- .../esp32_code/esp32_config.env.example | 3 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/.github/workflows/quakeguard-ci.yml b/.github/workflows/quakeguard-ci.yml index 191d939..e064895 100644 --- a/.github/workflows/quakeguard-ci.yml +++ b/.github/workflows/quakeguard-ci.yml @@ -117,4 +117,46 @@ jobs: run: | cd frontend-mobile-app # Run eslint on all JS/TS files - npx eslint . --ext .js,.jsx,.ts,.tsx \ No newline at end of file + npx eslint . --ext .js,.jsx,.ts,.tsx + + # TASK #216: Add iot-ci job + iot-ci: + name: IoT Firmware Build Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Source Code + uses: actions/checkout@v3 + + # We use Python to install the official PlatformIO Core CLI + - name: Setup Python Environment + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Cache PlatformIO Packages + uses: actions/cache@v3 + with: + path: ~/.platformio + key: ${{ runner.os }}-pio + restore-keys: | + ${{ runner.os }}-pio + + # TASK #217: Install PlatformIO Core + - name: Install PlatformIO CLI + run: | + python -m pip install --upgrade pip + pip install platformio + + # TASK #219: Inject dummy ENROLLMENT_TOKEN and configs + - name: Inject CI Environment Variables + run: | + cd iot-data-harvester/esp32_code + cp esp32_config.env.example esp32_config.env + + # TASK #220: Run pio run to validate compilation + - name: Validate Firmware Compilation + run: | + cd iot-data-harvester/esp32_code + # This compiles the firmware based on platformio.ini but does not upload it + pio run \ No newline at end of file diff --git a/iot-data-harvester/esp32_code/esp32_config.env.example b/iot-data-harvester/esp32_code/esp32_config.env.example index 2742790..0c07564 100644 --- a/iot-data-harvester/esp32_code/esp32_config.env.example +++ b/iot-data-harvester/esp32_code/esp32_config.env.example @@ -36,4 +36,5 @@ SENSOR_ID=101 # Keys are automatically generated by the firmware upon first initialization # and stored securely in the device's NVS (Non-Volatile Storage) partition. # -# Check the Serial Monitor output on boot to retrieve the Public Key. \ No newline at end of file +# Check the Serial Monitor output on boot to retrieve the Public Key. +ENROLLMENT_TOKEN=key \ No newline at end of file From 026a3dd66ab6df86457f5c7a4b955d5f3bcd81b0 Mon Sep 17 00:00:00 2001 From: GiZano Date: Thu, 23 Apr 2026 23:43:41 +0200 Subject: [PATCH 2/3] ci: revamp CI pipelines with path filtering, IoT build validation, and secure credentials - Remove monolithic `quakeguard-ci.yml` to prevent duplicate and noisy pipeline executions. - Split CI into `backend-ci.yml`, `frontend-ci.yml`, and `iot-ci.yml` with path-based triggers to drastically reduce wasted CI minutes. - Introduce PlatformIO build validation in `iot-ci.yml` to automatically catch C++ syntax and configuration errors. - Create `esp32_config.env.example` and inject dummy `ENROLLMENT_TOKEN` to satisfy IoT compile-time `#error` checks. - Refactor `backend-ci.yml` to replace hardcoded dummy database credentials with GitHub Actions `${{ secrets.* }}`. - Update `pg_isready` readiness check in the backend pipeline to utilize dynamic secret injection. Closes #215, Closes #216, Closes #217, Closes #218, Closes #219, Closes #220, Closes #221 Closes #222, Closes #223, Closes #224, Closes #225, Closes #226, Closes #227, Closes #228 --- .github/workflows/backend-ci.yml | 77 +++++++++++++ .github/workflows/frontend-ci.yml | 50 +++++++++ .github/workflows/iot-ci.yml | 50 +++++++++ .github/workflows/quakeguard-ci.yml | 162 ---------------------------- 4 files changed, 177 insertions(+), 162 deletions(-) create mode 100644 .github/workflows/backend-ci.yml create mode 100644 .github/workflows/frontend-ci.yml create mode 100644 .github/workflows/iot-ci.yml delete mode 100644 .github/workflows/quakeguard-ci.yml diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml new file mode 100644 index 0000000..34edb91 --- /dev/null +++ b/.github/workflows/backend-ci.yml @@ -0,0 +1,77 @@ +name: Backend CI + +on: + push: + branches: [ "main", "develop" ] + paths: + - 'backend-data-elaborator/**' + - '.github/workflows/backend-ci.yml' + pull_request: + branches: [ "main" ] + paths: + - 'backend-data-elaborator/**' + - '.github/workflows/backend-ci.yml' + +jobs: + backend-ci: + name: Backend Test & Security + runs-on: ubuntu-latest + + steps: + - name: Checkout Source Code + uses: actions/checkout@v3 + + - name: Setup Python Environment + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Cache Pip Dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('backend-data-elaborator/api/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install Python Dependencies & Security Tools + run: | + python -m pip install --upgrade pip + pip install bandit safety aiohttp ecdsa redis sqlalchemy psycopg2-binary geoalchemy2 + if [ -f backend-data-elaborator/api/requirements.txt ]; then pip install -r backend-data-elaborator/api/requirements.txt; fi + + - name: Run Bandit (Static Security Linter) + run: bandit -r backend-data-elaborator/api/src + + - name: Run Safety (Dependency Vulnerability Scanner) + run: if [ -f backend-data-elaborator/api/requirements.txt ]; then safety check -r backend-data-elaborator/api/requirements.txt; fi + + - name: Initialize Docker Infrastructure + env: + POSTGRES_DB: ${{ secrets.CI_POSTGRES_DB }} + POSTGRES_USER: ${{ secrets.CI_POSTGRES_USER }} + POSTGRES_PASSWORD: ${{ secrets.CI_POSTGRES_PASSWORD }} + API_PORT: 8000 + run: | + cd backend-data-elaborator/api + docker compose up -d --build + echo "Waiting for database readiness..." + # Updated to use the secret reference for the user flag + timeout 30s bash -c 'until docker compose exec -T postgres pg_isready -U ${{ secrets.CI_POSTGRES_USER }}; do sleep 2; done' + docker compose ps + + - name: Execute Critical Stress Test + env: + API_URL: "http://localhost:8000" + NUM_SENSORS: 150 + CONCURRENCY_LIMIT: 50 + run: | + cd backend-data-elaborator/api + export PYTHONPATH=$PYTHONPATH:. + python -m tests.stress_test + + - name: Retrieve System Logs on Failure + if: failure() + run: | + cd backend-data-elaborator/api + docker compose logs \ No newline at end of file diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml new file mode 100644 index 0000000..b58001c --- /dev/null +++ b/.github/workflows/frontend-ci.yml @@ -0,0 +1,50 @@ +name: Frontend CI + +on: + push: + branches: [ "main", "develop" ] + paths: + - 'frontend-mobile-app/**' + - '.github/workflows/frontend-ci.yml' + pull_request: + branches: [ "main" ] + paths: + - 'frontend-mobile-app/**' + - '.github/workflows/frontend-ci.yml' + +jobs: + frontend-ci: + name: Frontend Lint & Audit + runs-on: ubuntu-latest + + steps: + - name: Checkout Source Code + uses: actions/checkout@v3 + + - name: Setup Node.js Environment + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Cache Node Modules + uses: actions/cache@v3 + with: + path: frontend-mobile-app/node_modules + key: ${{ runner.os }}-node-${{ hashFiles('frontend-mobile-app/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install JS Dependencies + run: | + cd frontend-mobile-app + npm install + + - name: NPM Security Audit + run: | + cd frontend-mobile-app + npm audit --audit-level=high + + - name: ESLint Code Quality Check + run: | + cd frontend-mobile-app + npx eslint . --ext .js,.jsx,.ts,.tsx \ No newline at end of file diff --git a/.github/workflows/iot-ci.yml b/.github/workflows/iot-ci.yml new file mode 100644 index 0000000..bea0c85 --- /dev/null +++ b/.github/workflows/iot-ci.yml @@ -0,0 +1,50 @@ +name: IoT CI + +on: + push: + branches: [ "main", "develop" ] + paths: + - 'iot-data-harvester/**' + - '.github/workflows/iot-ci.yml' + pull_request: + branches: [ "main" ] + paths: + - 'iot-data-harvester/**' + - '.github/workflows/iot-ci.yml' + +jobs: + iot-ci: + name: IoT Firmware Build Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Source Code + uses: actions/checkout@v3 + + - name: Setup Python Environment + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Cache PlatformIO Packages + uses: actions/cache@v3 + with: + path: ~/.platformio + key: ${{ runner.os }}-pio + restore-keys: | + ${{ runner.os }}-pio + + - name: Install PlatformIO CLI + run: | + python -m pip install --upgrade pip + pip install platformio + + - name: Inject CI Environment Variables + run: | + cd iot-data-harvester/esp32_code + cp esp32_config.env.example esp32_config.env + + - name: Validate Firmware Compilation + run: | + cd iot-data-harvester/esp32_code + pio run \ No newline at end of file diff --git a/.github/workflows/quakeguard-ci.yml b/.github/workflows/quakeguard-ci.yml deleted file mode 100644 index e064895..0000000 --- a/.github/workflows/quakeguard-ci.yml +++ /dev/null @@ -1,162 +0,0 @@ -name: QuakeGuard CI Pipeline - -on: - push: - branches: [ "main", "develop" ] - pull_request: - branches: [ "main" ] - -jobs: - backend-ci: - name: Backend Test & Security - runs-on: ubuntu-latest - - steps: - - name: Checkout Source Code - uses: actions/checkout@v3 - - - name: Setup Python Environment - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - # TASK #40: Python Dependency Caching - - name: Cache Pip Dependencies - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('backend-data-elaborator/api/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install Python Dependencies & Security Tools - run: | - python -m pip install --upgrade pip - pip install bandit safety aiohttp ecdsa redis sqlalchemy psycopg2-binary geoalchemy2 - # Install exact requirements to test against vulnerabilities - if [ -f backend-data-elaborator/api/requirements.txt ]; then pip install -r backend-data-elaborator/api/requirements.txt; fi - - # TASK #41: Backend Security Auditing - - name: Run Bandit (Static Security Linter) - run: | - # Analyze the src directory for insecure code patterns - bandit -r backend-data-elaborator/api/src - - - name: Run Safety (Dependency Vulnerability Scanner) - run: | - # Check requirements for known CVEs - if [ -f backend-data-elaborator/api/requirements.txt ]; then safety check -r backend-data-elaborator/api/requirements.txt; fi - - # Existing Infrastructure Setup - - name: Initialize Docker Infrastructure - env: - POSTGRES_DB: monitoraggio_db - POSTGRES_USER: developer - POSTGRES_PASSWORD: development_pass - API_PORT: 8000 - run: | - cd backend-data-elaborator/api - docker compose up -d --build - echo "Waiting for database readiness..." - timeout 30s bash -c 'until docker compose exec -T postgres pg_isready -U developer; do sleep 2; done' - docker compose ps - - # Existing Stress Test - - name: Execute Critical Stress Test - env: - API_URL: "http://localhost:8000" - NUM_SENSORS: 150 - CONCURRENCY_LIMIT: 50 - run: | - cd backend-data-elaborator/api - export PYTHONPATH=$PYTHONPATH:. - python -m tests.stress_test - - - name: Retrieve System Logs on Failure - if: failure() - run: | - cd backend-data-elaborator/api - docker compose logs - - frontend-ci: - name: Frontend Lint & Audit - runs-on: ubuntu-latest - - steps: - - name: Checkout Source Code - uses: actions/checkout@v3 - - - name: Setup Node.js Environment - uses: actions/setup-node@v3 - with: - node-version: '18' - - # TASK #40: Javascript Dependency Caching - - name: Cache Node Modules - uses: actions/cache@v3 - with: - path: frontend-mobile-app/node_modules - key: ${{ runner.os }}-node-${{ hashFiles('frontend-mobile-app/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Install JS Dependencies - run: | - cd frontend-mobile-app - npm install - - # TASK #42: Frontend Security Audit - - name: NPM Security Audit - run: | - cd frontend-mobile-app - # Fails the pipeline if vulnerabilities high or critical are found - npm audit --audit-level=high - - # TASK #42: Frontend Linting - - name: ESLint Code Quality Check - run: | - cd frontend-mobile-app - # Run eslint on all JS/TS files - npx eslint . --ext .js,.jsx,.ts,.tsx - - # TASK #216: Add iot-ci job - iot-ci: - name: IoT Firmware Build Validation - runs-on: ubuntu-latest - - steps: - - name: Checkout Source Code - uses: actions/checkout@v3 - - # We use Python to install the official PlatformIO Core CLI - - name: Setup Python Environment - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Cache PlatformIO Packages - uses: actions/cache@v3 - with: - path: ~/.platformio - key: ${{ runner.os }}-pio - restore-keys: | - ${{ runner.os }}-pio - - # TASK #217: Install PlatformIO Core - - name: Install PlatformIO CLI - run: | - python -m pip install --upgrade pip - pip install platformio - - # TASK #219: Inject dummy ENROLLMENT_TOKEN and configs - - name: Inject CI Environment Variables - run: | - cd iot-data-harvester/esp32_code - cp esp32_config.env.example esp32_config.env - - # TASK #220: Run pio run to validate compilation - - name: Validate Firmware Compilation - run: | - cd iot-data-harvester/esp32_code - # This compiles the firmware based on platformio.ini but does not upload it - pio run \ No newline at end of file From b1318f38c202898126555be122a21e79aaabf07b Mon Sep 17 00:00:00 2001 From: GiZano Date: Thu, 23 Apr 2026 23:58:37 +0200 Subject: [PATCH 3/3] ci(core): enforce semantic PRs, commit messages, and validate workflows - Add `.commitlintrc.json` enforcing conventional commit types and scopes. - Inject `wagoid/commitlint-github-action` into backend, frontend, and IoT workflows to validate commit history on push. - Create `pr-lint.yml` using `amannn/action-semantic-pull-request` to require strict semantic PR titles (e.g., `feat(backend): ...`). - Create `devops-ci.yml` triggering strictly on `.github/workflows/**` modifications. - Implement `reviewdog/action-actionlint` to statically analyze YAML syntax, detect undefined secrets, and prevent broken pipelines. Closes #229, Closes #230, Closes #231, Closes #232, Closes #233, Closes #234, Closes #235 --- .commitlintrc.json | 15 +++++++++++++++ .github/workflows/backend-ci.yml | 2 ++ .github/workflows/deploy.yml | 2 ++ .github/workflows/devops-ci.yml | 22 ++++++++++++++++++++++ .github/workflows/frontend-ci.yml | 2 ++ .github/workflows/pr-lint.yml | 27 +++++++++++++++++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 .commitlintrc.json create mode 100644 .github/workflows/devops-ci.yml create mode 100644 .github/workflows/pr-lint.yml diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 0000000..8957319 --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,15 @@ +{ + "extends": ["@commitlint/config-conventional"], + "rules": { + "type-enum": [ + 2, + "always", + ["feat", "fix", "sec", "test", "refactor", "chore", "docs", "ci"] + ], + "scope-enum": [ + 2, + "always", + ["backend", "frontend", "iot", "ci", "deps", "core"] + ] + } +} \ No newline at end of file diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 34edb91..7e64d29 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -20,6 +20,8 @@ jobs: steps: - name: Checkout Source Code uses: actions/checkout@v3 + - name: Validate Commit Messages + uses: wagoid/commitlint-github-action@v5 - name: Setup Python Environment uses: actions/setup-python@v4 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f8f0cef..e5d5b92 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -29,6 +29,8 @@ jobs: # Step 1: Check out the repository code. - name: Checkout repository uses: actions/checkout@v4 + - name: Validate Commit Messages + uses: wagoid/commitlint-github-action@v5 # Step 2: Authenticate with the GitHub Container Registry. - name: Log in to the Container Registry diff --git a/.github/workflows/devops-ci.yml b/.github/workflows/devops-ci.yml new file mode 100644 index 0000000..e20d15f --- /dev/null +++ b/.github/workflows/devops-ci.yml @@ -0,0 +1,22 @@ +name: DevOps CI + +on: + push: + branches: [ "main", "develop" ] + paths: + - '.github/workflows/**' + pull_request: + branches: [ "main" ] + paths: + - '.github/workflows/**' + +jobs: + actionlint: + name: Validate GitHub Actions Workflows + runs-on: ubuntu-latest + steps: + - name: Checkout Source Code + uses: actions/checkout@v3 + + - name: Run Actionlint + uses: reviewdog/action-actionlint@v1 \ No newline at end of file diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml index b58001c..23bd335 100644 --- a/.github/workflows/frontend-ci.yml +++ b/.github/workflows/frontend-ci.yml @@ -20,6 +20,8 @@ jobs: steps: - name: Checkout Source Code uses: actions/checkout@v3 + - name: Validate Commit Messages + uses: wagoid/commitlint-github-action@v5 - name: Setup Node.js Environment uses: actions/setup-node@v3 diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml new file mode 100644 index 0000000..97648a0 --- /dev/null +++ b/.github/workflows/pr-lint.yml @@ -0,0 +1,27 @@ +name: PR Lint + +on: + pull_request: + types: + - opened + - edited + - synchronize + +jobs: + pr-title-validation: + name: Validate PR Title + runs-on: ubuntu-latest + steps: + - name: Semantic PR Check + uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + scopes: | + backend + frontend + iot + ci + deps + core + requireScope: true \ No newline at end of file