Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
308 lines
11 KiB
YAML
308 lines
11 KiB
YAML
name: Code Quality
|
|
permissions:
|
|
contents: read
|
|
|
|
on:
|
|
push:
|
|
branches: [ main, develop ]
|
|
pull_request:
|
|
branches: [ main ]
|
|
|
|
jobs:
|
|
code-quality:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
- name: Set up Python 3.10
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.10'
|
|
|
|
- name: Cache pip dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.cache/pip
|
|
key: ${{ runner.os }}-pip-quality-${{ hashFiles('**/requirements-dev.txt') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-pip-quality-
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements-dev.txt
|
|
|
|
- name: Format check with Black
|
|
run: |
|
|
echo "Checking code formatting with Black..."
|
|
black --check --diff .
|
|
echo "✓ Black formatting check passed"
|
|
|
|
- name: Lint with flake8
|
|
run: |
|
|
echo "Linting with flake8..."
|
|
# Stop the build if there are Python syntax errors or undefined names
|
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
# Exit-zero treats all errors as warnings. GitHub editor is 127 chars wide
|
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
|
echo "✓ flake8 linting passed"
|
|
|
|
- name: Type checking with mypy
|
|
run: |
|
|
echo "Type checking with mypy..."
|
|
# Only check our main package, ignore test files for now
|
|
mypy kikotools/ --ignore-missing-imports --no-strict-optional || true
|
|
echo "✓ mypy type checking completed"
|
|
|
|
- name: Check import structure
|
|
run: |
|
|
python -c "
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.getcwd())
|
|
|
|
# Test that all imports work correctly
|
|
try:
|
|
from kikotools import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
|
print('✓ Package-level imports successful')
|
|
except ImportError as e:
|
|
print(f'Warning: Package-level imports failed: {e}')
|
|
# This is expected since we don't have ComfyUI installed
|
|
|
|
# Test individual module imports
|
|
from kikotools.base import ComfyAssetsBaseNode
|
|
from kikotools.tools.resolution_calculator import ResolutionCalculatorNode
|
|
from kikotools.tools.resolution_calculator.logic import extract_dimensions
|
|
from kikotools.tools.resolution_calculator.node import ResolutionCalculatorNode as NodeClass
|
|
|
|
# Test Width Height Selector imports
|
|
from kikotools.tools.width_height_selector import WidthHeightSelectorNode
|
|
from kikotools.tools.width_height_selector.logic import get_preset_dimensions
|
|
from kikotools.tools.width_height_selector.presets import PRESET_OPTIONS, PRESET_METADATA
|
|
|
|
# Test Sampler Combo imports
|
|
from kikotools.tools.sampler_combo import SamplerComboNode
|
|
from kikotools.tools.sampler_combo.logic import get_sampler_combo, SAMPLERS, SCHEDULERS
|
|
|
|
# Test Seed History imports
|
|
from kikotools.tools.seed_history import SeedHistoryNode
|
|
from kikotools.tools.seed_history.logic import generate_random_seed, validate_seed_value
|
|
|
|
# Test Kiko Save Image imports
|
|
from kikotools.tools.kiko_save_image import KikoSaveImageNode
|
|
from kikotools.tools.kiko_save_image.logic import process_image_batch, validate_save_inputs
|
|
|
|
print('✓ All module imports successful')
|
|
"
|
|
|
|
- name: Check code style consistency
|
|
run: |
|
|
echo "Checking code style consistency..."
|
|
|
|
# Check for consistent naming
|
|
find kikotools/ -name "*.py" -exec grep -l "class.*Node" {} \; | while read file; do
|
|
if ! grep -q "ComfyAssetsBaseNode" "$file" && ! grep -q "class ComfyAssetsBaseNode" "$file"; then
|
|
echo "Checking $file for ComfyUI node inheritance..."
|
|
fi
|
|
done
|
|
|
|
# Check for proper docstrings
|
|
python -c "
|
|
import ast
|
|
import os
|
|
|
|
def check_docstrings(filepath):
|
|
with open(filepath, 'r') as f:
|
|
tree = ast.parse(f.read())
|
|
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
|
|
if not ast.get_docstring(node) and not node.name.startswith('_'):
|
|
print(f'Warning: {filepath}:{node.lineno} - {node.name} missing docstring')
|
|
|
|
for root, dirs, files in os.walk('kikotools'):
|
|
for file in files:
|
|
if file.endswith('.py') and not file.startswith('__'):
|
|
filepath = os.path.join(root, file)
|
|
check_docstrings(filepath)
|
|
|
|
print('✓ Docstring check completed')
|
|
"
|
|
|
|
security:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
- name: Set up Python 3.10
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.10'
|
|
|
|
- name: Install bandit
|
|
run: |
|
|
pip install bandit[toml]
|
|
|
|
- name: Run security scan with bandit
|
|
run: |
|
|
echo "Running security scan..."
|
|
bandit -r kikotools/ -ll || true
|
|
echo "✓ Security scan completed"
|
|
|
|
- name: Check for hardcoded secrets
|
|
run: |
|
|
echo "Checking for potential secrets..."
|
|
|
|
# Check for common secret patterns
|
|
if grep -r -i "password\|secret\|key\|token" kikotools/ --include="*.py" | grep -v "# " | grep -v "def " | grep -v "class "; then
|
|
echo "Warning: Potential hardcoded secrets found"
|
|
else
|
|
echo "✓ No obvious hardcoded secrets found"
|
|
fi
|
|
|
|
architecture:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
- 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-dev.txt
|
|
|
|
- name: Check architecture compliance
|
|
run: |
|
|
python -c "
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.getcwd())
|
|
|
|
print('Checking architecture compliance...')
|
|
|
|
# Test separation of concerns
|
|
from kikotools.tools.resolution_calculator import logic, node
|
|
|
|
# Logic module should not import node-specific things
|
|
import inspect
|
|
logic_source = inspect.getsource(logic)
|
|
|
|
if 'ComfyUI' in logic_source and 'INPUT_TYPES' not in logic_source:
|
|
print('⚠️ Warning: Logic module contains ComfyUI-specific code')
|
|
else:
|
|
print('✓ Logic module properly separated')
|
|
|
|
# Node module should inherit from base
|
|
from kikotools.tools.resolution_calculator.node import ResolutionCalculatorNode
|
|
from kikotools.base import ComfyAssetsBaseNode
|
|
|
|
if issubclass(ResolutionCalculatorNode, ComfyAssetsBaseNode):
|
|
print('✓ Node properly inherits from base class')
|
|
else:
|
|
print('❌ Node does not inherit from base class')
|
|
sys.exit(1)
|
|
|
|
# Check that nodes have proper ComfyUI interface
|
|
required_attrs = ['INPUT_TYPES', 'RETURN_TYPES', 'RETURN_NAMES', 'FUNCTION', 'CATEGORY']
|
|
|
|
# Test Resolution Calculator Node
|
|
for attr in required_attrs:
|
|
if not hasattr(ResolutionCalculatorNode, attr):
|
|
print(f'❌ ResolutionCalculatorNode missing required attribute: {attr}')
|
|
sys.exit(1)
|
|
|
|
# Test Width Height Selector Node
|
|
from kikotools.tools.width_height_selector.node import WidthHeightSelectorNode
|
|
|
|
if issubclass(WidthHeightSelectorNode, ComfyAssetsBaseNode):
|
|
print('✓ WidthHeightSelectorNode properly inherits from base class')
|
|
else:
|
|
print('❌ WidthHeightSelectorNode does not inherit from base class')
|
|
sys.exit(1)
|
|
|
|
for attr in required_attrs:
|
|
if not hasattr(WidthHeightSelectorNode, attr):
|
|
print(f'❌ WidthHeightSelectorNode missing required attribute: {attr}')
|
|
sys.exit(1)
|
|
|
|
# Test Sampler Combo Node
|
|
from kikotools.tools.sampler_combo.node import SamplerComboNode
|
|
|
|
if issubclass(SamplerComboNode, ComfyAssetsBaseNode):
|
|
print('✓ SamplerComboNode properly inherits from base class')
|
|
else:
|
|
print('❌ SamplerComboNode does not inherit from base class')
|
|
sys.exit(1)
|
|
|
|
for attr in required_attrs:
|
|
if not hasattr(SamplerComboNode, attr):
|
|
print(f'❌ SamplerComboNode missing required attribute: {attr}')
|
|
sys.exit(1)
|
|
|
|
# Test Seed History Node
|
|
from kikotools.tools.seed_history.node import SeedHistoryNode
|
|
|
|
if issubclass(SeedHistoryNode, ComfyAssetsBaseNode):
|
|
print('✓ SeedHistoryNode properly inherits from base class')
|
|
else:
|
|
print('❌ SeedHistoryNode does not inherit from base class')
|
|
sys.exit(1)
|
|
|
|
for attr in required_attrs:
|
|
if not hasattr(SeedHistoryNode, attr):
|
|
print(f'❌ SeedHistoryNode missing required attribute: {attr}')
|
|
sys.exit(1)
|
|
|
|
# Test Kiko Save Image Node
|
|
from kikotools.tools.kiko_save_image.node import KikoSaveImageNode
|
|
|
|
if issubclass(KikoSaveImageNode, ComfyAssetsBaseNode):
|
|
print('✓ KikoSaveImageNode properly inherits from base class')
|
|
else:
|
|
print('❌ KikoSaveImageNode does not inherit from base class')
|
|
sys.exit(1)
|
|
|
|
# KikoSaveImage is an output node, so it doesn't have RETURN_TYPES/RETURN_NAMES
|
|
save_required_attrs = ['INPUT_TYPES', 'FUNCTION', 'CATEGORY']
|
|
for attr in save_required_attrs:
|
|
if not hasattr(KikoSaveImageNode, attr):
|
|
print(f'❌ KikoSaveImageNode missing required attribute: {attr}')
|
|
sys.exit(1)
|
|
|
|
# Check that it's properly marked as an output node
|
|
if not hasattr(KikoSaveImageNode, 'OUTPUT_NODE') or not KikoSaveImageNode.OUTPUT_NODE:
|
|
print('❌ KikoSaveImageNode missing OUTPUT_NODE = True')
|
|
sys.exit(1)
|
|
|
|
print('✓ All architecture checks passed for all tools')
|
|
"
|
|
|
|
- name: Check test coverage expectations
|
|
run: |
|
|
python -c "
|
|
import os
|
|
|
|
# Count test files vs implementation files
|
|
test_files = 0
|
|
impl_files = 0
|
|
|
|
for root, dirs, files in os.walk('tests'):
|
|
test_files += len([f for f in files if f.startswith('test_') and f.endswith('.py')])
|
|
|
|
for root, dirs, files in os.walk('kikotools'):
|
|
impl_files += len([f for f in files if f.endswith('.py') and not f.startswith('__')])
|
|
|
|
print(f'Implementation files: {impl_files}')
|
|
print(f'Test files: {test_files}')
|
|
|
|
if test_files >= impl_files * 0.5: # At least 50% test coverage by file count
|
|
print('✓ Adequate test file coverage')
|
|
else:
|
|
print('⚠️ Warning: Low test file coverage')
|
|
"
|