Skip to content

Feat/workflow hardening#83

Merged
DaTiC0 merged 6 commits into
mainfrom
feat/workflow-hardening
Mar 7, 2026
Merged

Feat/workflow hardening#83
DaTiC0 merged 6 commits into
mainfrom
feat/workflow-hardening

Conversation

@DaTiC0

@DaTiC0 DaTiC0 commented Mar 7, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Add runtime health endpoint, local tooling, and workflow safeguards to harden development and deployment practices.

New Features:

  • Expose a /health endpoint reporting MQTT connection status with appropriate HTTP status codes.
  • Introduce a Makefile with common commands for running the app, tests, health checks, and installing git hooks.

Enhancements:

  • Make MQTT configuration more flexible by reading broker port and TLS settings from environment variables and enforcing TLS v1.2.
  • Document health checks, recommended collaboration workflow, and recovery steps for accidental commits to main in the README and new CONTRIBUTING guide.

CI:

  • Add a GitHub Actions workflow to fail builds when local absolute file system paths are detected in tracked files.

Documentation:

  • Add CONTRIBUTING guidelines covering branching strategy, local flow, PR requirements, and AI-assisted code review policy.
  • Add a pull request template guiding validation, AI-assisted review declarations, and risk/rollback planning.

Chores:

  • Add scripts for automated health checks, absolute path scanning, and git hook installation, and wire them into local and CI workflows.

@sourcery-ai

sourcery-ai Bot commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a runtime /health endpoint integrated with MQTT status, introduces a standardized local development workflow with Makefile targets and git hooks, hardens MQTT configuration via environment-driven settings and TLS, and sets up contribution documentation plus CI and local tooling to enforce path safety and review hygiene.

Sequence diagram for the new /health runtime check

sequenceDiagram
    actor Developer
    participant Shell as LocalShell
    participant HealthScript as health_check_sh
    participant Flask as FlaskApp
    participant MQTT as MQTTClient

    Developer->>LocalShell: run make health
    LocalShell->>HealthScript: bash scripts/health_check.sh
    HealthScript->>Flask: HTTP GET /health
    Flask->>MQTT: read connected attribute
    MQTT-->>Flask: connected (true|false)
    alt MQTT connected
        Flask-->>HealthScript: 200 {status: ok, service: smart-google, mqtt_connected: true}
        HealthScript-->>LocalShell: exit 0 (Health check passed)
    else MQTT not connected
        Flask-->>HealthScript: 503 {status: degraded, service: smart-google, mqtt_connected: false}
        HealthScript-->>LocalShell: exit 1 (Health check failed)
    end
    LocalShell-->>Developer: show health result and HTTP body
Loading

File-Level Changes

Change Details Files
Expose a runtime health endpoint that reports MQTT connectivity and is scriptable for local and CI checks.
  • Import the MQTT notifications module into the Flask routes module
  • Add a /health route that returns JSON including service name, MQTT connection status, and appropriate HTTP status codes
  • Document the health endpoint and its usage in README, including a make-based shortcut
routes.py
README.md
Harden MQTT configuration to be environment-driven and TLS-capable.
  • Make MQTT_BROKER_PORT configurable via the MQTT_BROKER_PORT environment variable with a default
  • Allow enabling MQTT TLS via MQTT_TLS_ENABLED environment variable with flexible truthy values
  • Specify MQTT_TLS_VERSION as TLS v1.2 via ssl.PROTOCOL_TLSv1_2
config.py
Introduce a standardized local workflow via Makefile targets and scripts, including health, path safety, and git hook installation.
  • Create Makefile with run, test, health, check-paths, and install-hooks targets, including pytest fallback to unittest and special handling for no-test exit code
  • Add scripts for health checking an HTTP endpoint with curl and enforcing non-fatal/no-output semantics for healthy responses
  • Add scripts to scan tracked files for machine-specific absolute paths and to configure core.hooksPath and pre-commit hook execution
Makefile
scripts/health_check.sh
scripts/check_absolute_paths.sh
scripts/install_git_hooks.sh
.githooks/pre-commit
Document and enforce a safer collaboration workflow and review policy, including AI-assisted review expectations and path safety checks in CI.
  • Add CONTRIBUTING.md describing branching strategy, local workflow, PR requirements, AI-assisted code policy, and recommended GitHub branch protection settings
  • Extend README with health-check usage, workflow guidance, and recovery instructions for accidental commits to main
  • Add a pull request template capturing summary, validation steps, AI-assisted review confirmation, and risk/rollback information
  • Create a GitHub Actions workflow to run the absolute-path safety script on pushes and pull requests to main
CONTRIBUTING.md
README.md
.github/pull_request_template.md
.github/workflows/path-safety.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the project's development workflow, maintainability, and security posture. It establishes clear contribution guidelines, automates pre-commit checks to enforce code quality and prevent common issues, and introduces a centralized command interface for development tasks. Additionally, it enhances the application's observability with a new health check endpoint and makes critical configurations more flexible.

Highlights

  • Workflow Hardening & Standardization: Introduced a CONTRIBUTING.md file with detailed guidelines for branching, local development flow, PR requirements, and AI-assisted code policy. A pull_request_template.md was also added to standardize PR submissions.
  • Automated Checks and Development Tools: Implemented a Makefile to centralize common development commands like run, test, health, check-paths, and install-hooks. A pre-commit Git hook was added to automatically run make check-paths before commits, preventing the introduction of absolute local paths.
  • Enhanced Security and Environment Management: Expanded the .gitignore file to include more local database files (*.sqlite, *.db), service account credentials (firebase-adminsdk.json, *.pem, *.key), and environment files (.env.*), improving security and reducing repository clutter.
  • Application Health Endpoint: Added a new /health endpoint to routes.py that reports the application's status, specifically its MQTT connection. A corresponding health_check.sh script was created to easily query this endpoint.
  • Flexible MQTT Configuration: Modified config.py to allow the MQTT broker port and TLS enablement to be configured via environment variables, providing greater flexibility for deployment and testing environments.
Changelog
  • .githooks/pre-commit
    • Added a pre-commit hook to run path checks.
  • .github/pull_request_template.md
    • Added a template for pull request descriptions.
  • .gitignore
    • Updated ignored files to include more local database, credential, and environment files.
  • CONTRIBUTING.md
    • Added comprehensive guidelines for contributing to the project.
  • Makefile
    • Added a Makefile to centralize common development commands.
  • README.md
    • Updated documentation to include health check, workflow, and recovery instructions.
  • config.py
    • Updated MQTT configuration to support environment variables for port and TLS settings.
  • routes.py
    • Added a new '/health' endpoint to expose application health status.
  • scripts/check_absolute_paths.sh
    • Added a script to identify and prevent absolute path commits.
  • scripts/health_check.sh
    • Added a script to perform a health check on the application.
  • scripts/install_git_hooks.sh
    • Added a script to install custom Git hooks.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/path-safety.yml
Activity
  • No specific activity (comments, reviews, progress) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The /health endpoint reaches into notifications.mqtt.connected directly; consider adding a small helper or interface on the MQTT client so the health check doesn’t depend on an internal attribute that might change or be unset.
  • In config.py, MQTT_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 is now unconditional; if TLS is disabled or ssl is not already imported in this module, this may be unnecessary or error-prone—consider importing ssl explicitly and only setting the version when MQTT_TLS_ENABLED is true or making it configurable.
  • The scripts/install_git_hooks.sh script assumes .githooks/pre-commit exists and is chmod-safe; you may want to add a guard that checks for the file’s existence before changing permissions to avoid failing in fresh clones or forks without that hook.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `/health` endpoint reaches into `notifications.mqtt.connected` directly; consider adding a small helper or interface on the MQTT client so the health check doesn’t depend on an internal attribute that might change or be unset.
- In `config.py`, `MQTT_TLS_VERSION = ssl.PROTOCOL_TLSv1_2` is now unconditional; if TLS is disabled or `ssl` is not already imported in this module, this may be unnecessary or error-prone—consider importing `ssl` explicitly and only setting the version when `MQTT_TLS_ENABLED` is true or making it configurable.
- The `scripts/install_git_hooks.sh` script assumes `.githooks/pre-commit` exists and is chmod-safe; you may want to add a guard that checks for the file’s existence before changing permissions to avoid failing in fresh clones or forks without that hook.

## Individual Comments

### Comment 1
<location path="config.py" line_range="26" />
<code_context>
     SQLALCHEMY_TRACK_MODIFICATIONS = False
     MQTT_BROKER_URL = environ.get('MQTT_BROKER_URL')
-    MQTT_BROKER_PORT = 11868
+    MQTT_BROKER_PORT = int(environ.get('MQTT_BROKER_PORT', 11868))
     MQTT_USERNAME = environ.get('MQTT_USERNAME')
     MQTT_PASSWORD = environ.get('MQTT_PASSWORD')
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider more defensive parsing of MQTT_BROKER_PORT to handle empty or invalid values.

If `MQTT_BROKER_PORT` is present but empty or non-numeric, `int(...)` will raise `ValueError` and prevent startup. Consider a small helper or try/except to fall back to the default or emit a clearer configuration error.

Suggested implementation:

```python
from dotenv import load_dotenv
from generate_service_account_file import generate_file


def _get_int_env(name, default):
    """
    Safely parse an integer environment variable.

    Returns the provided default if the variable is unset, empty, or invalid.
    """
    raw_value = environ.get(name)
    if raw_value is None or raw_value == "":
        return default
    try:
        return int(raw_value)
    except (TypeError, ValueError):
        return default

```

```python
    MQTT_BROKER_URL = environ.get('MQTT_BROKER_URL')
    MQTT_BROKER_PORT = _get_int_env('MQTT_BROKER_PORT', 11868)

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread config.py Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly hardens the development workflow by introducing a Makefile, git hooks for pre-commit checks, a pull request template, and comprehensive contributing guidelines. It also improves security by expanding the .gitignore to cover credentials and local artifacts, and makes the MQTT configuration more robust and environment-driven. I've added a couple of suggestions to further refine the test script logic in the Makefile and improve the TLS configuration for future-proofing.

Comment thread Makefile Outdated
Comment thread config.py Outdated
@DaTiC0

DaTiC0 commented Mar 7, 2026

Copy link
Copy Markdown
Owner Author

@copilot Please address the comments from this code review:

Overall Comments

  • The /health endpoint reaches into notifications.mqtt.connected directly; consider adding a small helper or interface on the MQTT client so the health check doesn’t depend on an internal attribute that might change or be unset.
  • In config.py, MQTT_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 is now unconditional; if TLS is disabled or ssl is not already imported in this module, this may be unnecessary or error-prone—consider importing ssl explicitly and only setting the version when MQTT_TLS_ENABLED is true or making it configurable.
  • The scripts/install_git_hooks.sh script assumes .githooks/pre-commit exists and is chmod-safe; you may want to add a guard that checks for the file’s existence before changing permissions to avoid failing in fresh clones or forks without that hook.

Individual Comments

Comment 1

SQLALCHEMY_TRACK_MODIFICATIONS = False MQTT_BROKER_URL = environ.get('MQTT_BROKER_URL') - MQTT_BROKER_PORT = 11868 + MQTT_BROKER_PORT = int(environ.get('MQTT_BROKER_PORT', 11868)) MQTT_USERNAME = environ.get('MQTT_USERNAME') MQTT_PASSWORD = environ.get('MQTT_PASSWORD') **suggestion (bug_risk):** Consider more defensive parsing of MQTT_BROKER_PORT to handle empty or invalid values.

If MQTT_BROKER_PORT is present but empty or non-numeric, int(...) will raise ValueError and prevent startup. Consider a small helper or try/except to fall back to the default or emit a clearer configuration error.

Suggested implementation:

from dotenv import load_dotenv
from generate_service_account_file import generate_file


def _get_int_env(name, default):
    """
    Safely parse an integer environment variable.

    Returns the provided default if the variable is unset, empty, or invalid.
    """
    raw_value = environ.get(name)
    if raw_value is None or raw_value == "":
        return default
    try:
        return int(raw_value)
    except (TypeError, ValueError):
        return default
    MQTT_BROKER_URL = environ.get('MQTT_BROKER_URL')
    MQTT_BROKER_PORT = _get_int_env('MQTT_BROKER_PORT', 11868)

</issue_to_address>

Copilot AI commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

@DaTiC0 I've opened a new pull request, #84, to work on those changes. Once the pull request is ready, I'll request review from you.

… guard (#84)

* Initial plan

* Address review feedback: MQTT helper, conditional TLS version, git hook guard

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>

* Remove unused mqtt import from routes.py (fixes DeepSource F401)

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>

Copilot AI commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

@DaTiC0 I've opened a new pull request, #85, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

@DaTiC0 I've opened a new pull request, #86, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

@DaTiC0 I've opened a new pull request, #87, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 3 commits March 7, 2026 18:54
* Initial plan

* Fix incorrect exit code 5 handling in unittest block of Makefile

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>
* Initial plan

* Use ssl.PROTOCOL_TLS_CLIENT instead of ssl.PROTOCOL_TLSv1_2 for TLS version negotiation

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>
* Initial plan

* Add _get_int_env helper for safe MQTT_BROKER_PORT parsing

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>

@DaTiC0 DaTiC0 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testing auto review and fixing

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens local development and CI workflows by adding a runtime health endpoint, introducing local helper tooling (scripts/Makefile/hooks), and enforcing a CI safeguard that blocks commits containing machine-specific absolute paths.

Changes:

  • Add /health endpoint that reports MQTT connectivity and returns 200/503 accordingly.
  • Add local tooling: Makefile targets, health/path-scan scripts, and a pre-commit hook installer.
  • Add CI workflow to scan tracked files for absolute local filesystem paths; expand docs/templates for contribution/PR hygiene.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
scripts/install_git_hooks.sh Configures repo-local git hooks path and ensures pre-commit hook is executable.
scripts/health_check.sh Adds a curl-based CLI health check helper.
scripts/check_absolute_paths.sh Adds tracked-file scan for common absolute path patterns.
routes.py Adds /health endpoint using centralized MQTT connectivity check.
notifications.py Adds is_mqtt_connected() helper around Flask-MQTT client state.
config.py Reads MQTT broker port from env and adds env-driven TLS enablement + TLS version selection.
README.md Documents health check usage and recommended collaboration workflow.
Makefile Adds common dev commands (run/test/health/path scan/hook install).
CONTRIBUTING.md Adds contributor workflow + PR requirements + AI-assisted review policy.
.gitignore Broadens ignoring of local DB/credential artifacts and .env.* files.
.github/workflows/path-safety.yml Adds CI job that fails if absolute local paths are detected.
.github/pull_request_template.md Adds PR checklist including validation and AI-assistance declarations.
.githooks/pre-commit Adds pre-commit hook to run the absolute-path scan locally.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

#!/usr/bin/env bash
set -euo pipefail

pattern='(/home/[[:alnum:]_.-]+/|/Users/[[:alnum:]_.-]+/|[A-Za-z]:\\\\Users\\\\)'

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows-path branch of the regex looks like it only matches doubled backslashes (e.g., C:\\Users\\...). Many files will contain Windows paths with single backslashes (C:\Users\...), so this scan can miss the very cases it’s meant to catch. Consider adjusting the pattern to match one-or-more backslashes between segments (and/or both escaped and unescaped variants).

Suggested change
pattern='(/home/[[:alnum:]_.-]+/|/Users/[[:alnum:]_.-]+/|[A-Za-z]:\\\\Users\\\\)'
pattern='(/home/[[:alnum:]_.-]+/|/Users/[[:alnum:]_.-]+/|[A-Za-z]:\\\\+Users\\\\+)'

Copilot uses AI. Check for mistakes.
Comment thread config.py
MQTT_KEEPALIVE = 90
MQTT_TLS_ENABLED = False # set TLS to disabled for testing purposes
MQTT_TLS_ENABLED = environ.get('MQTT_TLS_ENABLED', 'False').lower() in ('1', 'true', 'yes', 'on')
MQTT_TLS_VERSION = ssl.PROTOCOL_TLS_CLIENT if MQTT_TLS_ENABLED else None

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ssl.PROTOCOL_TLS_CLIENT negotiates the highest supported TLS version (may include TLS 1.3) and does not strictly enforce TLS v1.2 as described in the PR. If you need to pin TLS 1.2, set the TLS version accordingly (e.g., use the TLSv1.2 protocol constant that Flask-MQTT/paho expects, or configure min/max TLS version via an SSLContext if supported).

Suggested change
MQTT_TLS_VERSION = ssl.PROTOCOL_TLS_CLIENT if MQTT_TLS_ENABLED else None
MQTT_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 if MQTT_TLS_ENABLED else None

Copilot uses AI. Check for mistakes.
@DaTiC0
DaTiC0 merged commit 9f931f2 into main Mar 7, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants