Skip to content

Sync with main-repo - 2026-04-03 #17

Sync with main-repo - 2026-04-03

Sync with main-repo - 2026-04-03 #17

Workflow file for this run

# -*- coding: utf-8 -*-
# ============================================================================ #
# DockerDiscordControl (DDC) - Code Quality CI/CD Workflow #
# https://ddc.bot #
# Copyright (c) 2025 MAX #
# Licensed under the MIT License #
# ============================================================================ #
#
# This workflow runs code quality checks on every push and pull request.
# Includes cyclomatic complexity, maintainability index, linting, and type checking.
name: Code Quality
on:
push:
branches:
- main
- v2.0
- develop
paths:
- '**.py'
- '.pylintrc'
- '.flake8'
- 'mypy.ini'
- 'requirements*.txt'
- '.github/workflows/code-quality.yml'
pull_request:
branches:
- main
- v2.0
paths:
- '**.py'
- '.pylintrc'
- '.flake8'
- 'mypy.ini'
- 'requirements*.txt'
workflow_dispatch: # Allow manual trigger
permissions:
contents: read # Checkout code
actions: read # Download artifacts
pull-requests: write # Comment on PRs
security-events: write # Upload SARIF for SonarCloud
env:
# Quality gates thresholds
MIN_PYLINT_SCORE: "8.0"
MAX_COMPLEXITY: "10"
MIN_MAINTAINABILITY: "80"
jobs:
code-complexity:
name: Code Complexity Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install radon xenon
- name: Run Cyclomatic Complexity Check
run: |
echo "## Cyclomatic Complexity Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Target**: All functions should have complexity < 10 (Grade B or better)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Check complexity for all Python files
radon cc services/ app/ utils/ -a -s --total-average > complexity_report.txt || true
cat complexity_report.txt >> $GITHUB_STEP_SUMMARY
# Fail if average complexity is too high
radon cc services/ app/ utils/ --min B --total-average
continue-on-error: false
- name: Run Maintainability Index Check
run: |
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Maintainability Index Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Target**: Maintainability Index > 80 (Grade A)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Check maintainability index
radon mi services/ app/ utils/ -s > maintainability_report.txt || true
cat maintainability_report.txt >> $GITHUB_STEP_SUMMARY
# Fail if maintainability is too low
radon mi services/ app/ utils/ --min A
continue-on-error: false
- name: Run Xenon Complexity Monitor
run: |
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Xenon Complexity Monitor" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Xenon monitors code complexity - adjusted for production codebase
# Current thresholds (pragmatic for existing complex business logic):
# - max-absolute F: Monitor all functions (baseline established)
# - max-modules D: Modules should maintain reasonable organization
# - max-average B: Average complexity should be good across codebase
#
# ✅ Recent refactoring improvements (2025-01):
# • validate_new_task_input: F → C (complexity 70→12, -83% reduction)
# • _get_host_info: F → A (complexity 150→3, -98% reduction)
#
# 🎯 Future refactoring targets (current F-ranked):
# • services/scheduling/scheduler.py:477 calculate_next_run
# • services/infrastructure/container_status_service.py:310 _fetch_container_status
# • app/utils/port_diagnostics.py:440 _get_actual_host_ip
xenon --max-absolute F --max-modules D --max-average B services/ app/ utils/
continue-on-error: false
- name: Upload complexity reports
if: always()
uses: actions/upload-artifact@v4
with:
name: complexity-reports
path: |
complexity_report.txt
maintainability_report.txt
retention-days: 30
pylint:
name: Pylint Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt
- name: Run Pylint
run: |
echo "## Pylint Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Target**: Pylint score > ${{ env.MIN_PYLINT_SCORE }}/10" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Run pylint and capture score
pylint services/ app/ utils/ --output-format=text --score=yes > pylint_report.txt || true
cat pylint_report.txt >> $GITHUB_STEP_SUMMARY
# Extract score and fail if below threshold
SCORE=$(tail -n 2 pylint_report.txt | grep "Your code has been rated" | awk '{print $7}' | cut -d'/' -f1)
echo "Pylint Score: $SCORE"
if (( $(echo "$SCORE < ${{ env.MIN_PYLINT_SCORE }}" | bc -l) )); then
echo "❌ Pylint score $SCORE is below threshold ${{ env.MIN_PYLINT_SCORE }}"
exit 1
else
echo "✅ Pylint score $SCORE meets threshold ${{ env.MIN_PYLINT_SCORE }}"
fi
continue-on-error: false
- name: Upload pylint report
if: always()
uses: actions/upload-artifact@v4
with:
name: pylint-report
path: pylint_report.txt
retention-days: 30
flake8:
name: Flake8 Style Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 mccabe
- name: Run Flake8
run: |
echo "## Flake8 Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Checks**: PEP 8 style, complexity < ${{ env.MAX_COMPLEXITY }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Run flake8
flake8 services/ app/ utils/ --statistics --count > flake8_report.txt || true
cat flake8_report.txt >> $GITHUB_STEP_SUMMARY
# Fail if there are critical errors
flake8 services/ app/ utils/ --count --select=E9,F63,F7,F82 --show-source
continue-on-error: false
- name: Upload flake8 report
if: always()
uses: actions/upload-artifact@v4
with:
name: flake8-report
path: flake8_report.txt
retention-days: 30
mypy:
name: MyPy Type Checking
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt
- name: Run MyPy
run: |
echo "## MyPy Type Checking Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Checks**: Static type checking for type safety" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Run mypy
mypy --config-file mypy.ini > mypy_report.txt || true
cat mypy_report.txt >> $GITHUB_STEP_SUMMARY
# MyPy is currently informational only (progressive typing)
echo "ℹ️ MyPy errors are informational - progressive typing in progress"
continue-on-error: true # For now, mypy is informational
- name: Upload mypy report
if: always()
uses: actions/upload-artifact@v4
with:
name: mypy-report
path: mypy_report.txt
retention-days: 30
sonarcloud:
name: SonarCloud Analysis
runs-on: ubuntu-latest
if: github.repository == 'DockerDiscordControl/DockerDiscordControl' # Only for main repo
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for better analysis
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt
- name: Run tests with coverage
run: |
python -m pytest tests/ \
--cov=services \
--cov=app \
--cov=utils \
--cov-report=xml \
--cov-report=html
continue-on-error: true
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate in SonarCloud
with:
args: >
-Dsonar.projectKey=DockerDiscordControl_DockerDiscordControl
-Dsonar.organization=dockerdiscordcontrol
-Dsonar.python.coverage.reportPaths=coverage.xml
-Dsonar.sources=services,app,utils
-Dsonar.tests=tests
-Dsonar.python.version=3.9,3.10,3.11
continue-on-error: true # Don't fail build on SonarCloud issues
quality-summary:
name: Quality Summary
runs-on: ubuntu-latest
needs: [code-complexity, pylint, flake8, mypy]
if: always()
steps:
- name: Download all reports
uses: actions/download-artifact@v4
with:
path: reports/
- name: Generate quality summary
run: |
echo "# Code Quality Summary" > quality_summary.md
echo "" >> quality_summary.md
echo "**Branch**: ${{ github.ref_name }}" >> quality_summary.md
echo "**Commit**: ${{ github.sha }}" >> quality_summary.md
echo "**Date**: $(date)" >> quality_summary.md
echo "" >> quality_summary.md
# Job statuses
echo "## Job Results" >> quality_summary.md
echo "" >> quality_summary.md
echo "- Code Complexity: ${{ needs.code-complexity.result }}" >> quality_summary.md
echo "- Pylint: ${{ needs.pylint.result }}" >> quality_summary.md
echo "- Flake8: ${{ needs.flake8.result }}" >> quality_summary.md
echo "- MyPy: ${{ needs.mypy.result }}" >> quality_summary.md
echo "" >> quality_summary.md
# Overall status
if [[ "${{ needs.code-complexity.result }}" == "success" ]] && \
[[ "${{ needs.pylint.result }}" == "success" ]] && \
[[ "${{ needs.flake8.result }}" == "success" ]]; then
echo "## ✅ Overall: PASS" >> quality_summary.md
echo "All critical quality gates passed!" >> quality_summary.md
else
echo "## ❌ Overall: FAIL" >> quality_summary.md
echo "One or more quality gates failed. Please review the reports." >> quality_summary.md
exit 1
fi
cat quality_summary.md >> $GITHUB_STEP_SUMMARY
- name: Comment PR with quality results
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const summary = fs.readFileSync('quality_summary.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: summary
});