Files
8c338f6da7 feat: MCP Bridge Workbench, just command cleanup, testing, etc. (#24)
* fix: lots of fixes and name refactoring

* feat: Add workbench preferences

* fix: MCP bridge status widget and just command fixes

* fix(tests): Use the correct mesa-glx package

* fix(ci): Add fontconfig to GUI test dependencies

FreeCAD GUI was failing to start with:
"Fontconfig error: Cannot load default config file: No such file"

Added fontconfig and fonts-dejavu-core packages to the GUI test job
dependencies to resolve the font configuration issue.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(addon): Extract path utilities into shared module

Create path_utils.py module that consolidates duplicated path-finding
logic from commands.py and InitGui.py:
- get_addon_path(): Find addon directory with caching and fallbacks
- get_icon_path(): Get full path to an icon file
- get_icons_dir(): Get path to icons directory
- get_workbench_icon(): Get path to workbench main icon

This removes ~100 lines of duplicated code while preserving the same
behavior including _addon_path_cache and all fallback methods.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(addon): Prevent stale plugin state on startup failure

The StartMCPBridgeCommand.Activated method could leave _mcp_plugin in
a partially initialized state if FreecadMCPPlugin.start() failed after
the plugin was instantiated.

Changes:
- Create plugin in a local variable first
- Only assign to _mcp_plugin after start() succeeds
- Explicitly clear _mcp_plugin and _running_config in exception
  handlers to ensure clean state for subsequent retry attempts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Lot of broad improvements

* fix(ci): Use blocking headless_server.py for GUI tests

The GUI test was using startup_bridge.py which is non-blocking
(designed for interactive use). For CI, even in GUI mode, we need
the blocking headless_server.py that calls run_forever() to keep
FreeCAD running. GUI features are still available since we use
the 'freecad' executable instead of 'freecadcmd'.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(addon): Rename headless_server.py to blocking_bridge.py

The old name was misleading because:
- It works with both GUI (freecad) and headless (freecadcmd) modes
- The key characteristic is that it BLOCKS with run_forever()

New naming convention clarifies the difference:
- blocking_bridge.py: Starts bridge and blocks (for CI, servers)
- startup_bridge.py: Starts bridge and returns (for interactive GUI)

Updated all references across:
- GitHub workflow (macro-test.yaml)
- Just commands (freecad.just)
- Unit tests (test_addon_structure.py)
- Documentation (5 files)
- CLAUDE.md

Also improved the script to detect GUI mode dynamically using
FreeCAD.GuiUp and display the appropriate status message.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(just): Remove erroneous rm of startup_bridge.py on error

The startup script is now a permanent source file in the repository,
not a generated temporary file. The rm -f would have deleted source
code if FreeCAD wasn't found.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: General improvements

* fix: Lots of general fixes and only stable to PyPi

* fix: small cleanup

* fix: Small fixes and hopefully fixes the GUI tests

* fix: Add proper library paths for FreeCAD GUI in CI

- Create wrapper scripts instead of symlinks for AppImage binaries
- Set LD_LIBRARY_PATH, QT_PLUGIN_PATH for GUI mode
- Add diagnostic output to identify startup failures

* fix: Use apprun for GUI tests in CI

* fix: Improving Xvfb tests

* fix: GUI tests worlk

* chore: remove invalid --no-splash comments

* fix: ARM64 architecture support and other fixes

* fix: cleanup

* test: just commands test suite

* test: improve just command tests

* fix: more general improvements

* fix: more cleanup

* fix: more updates

* fix: small tweaks

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 15:26:33 -08:00

317 lines
11 KiB
YAML

# Pre-commit hooks configuration
# https://pre-commit.com/
default_language_version:
python: python3.11 # Must match FreeCAD's bundled Python version
repos:
# ==========================================================================
# General File Hygiene
# ==========================================================================
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
exclude: \.md$ # Allow trailing spaces in markdown for line breaks
- id: end-of-file-fixer
exclude: \.safety-project\.ini$ # Safety CLI manages its own formatting
- id: check-xml
- id: check-yaml
args: [--unsafe]
- id: check-toml
- id: check-json
exclude: ^\.vscode/.*\.json$ # VS Code uses JSONC (JSON with Comments)
- id: check-added-large-files
args: [--maxkb=1000]
- id: check-merge-conflict
- id: check-case-conflict
- id: check-symlinks
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
- id: detect-private-key
- id: mixed-line-ending
args: [--fix=lf]
- id: no-commit-to-branch
args: [--branch, main, --branch, master]
- id: check-ast # Check Python syntax
types: [text]
files: \.(py|FCMacro)$
# JSON5/JSONC validation for VS Code config files (supports comments)
- repo: https://github.com/maresb/check-json5
rev: v1.0.1
hooks:
- id: check-json5
files: ^\.vscode/.*\.json$ # Only check VS Code JSONC files
# ==========================================================================
# Python - Linting and Formatting
# ==========================================================================
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.11
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
types_or: [python, text]
files: \.(py|FCMacro)$
- id: ruff-format
types_or: [python, text]
files: \.(py|FCMacro)$
# ==========================================================================
# Python - Type Checking
# ==========================================================================
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.19.1
hooks:
- id: mypy
additional_dependencies:
- pydantic>=2.10.0
- pydantic-settings>=2.7.0
- mcp>=1.25.0
# Note: mypy doesn't natively support .FCMacro, so we skip those files
# FCMacro files are checked by ruff and bandit instead
args: [--config-file=pyproject.toml]
# ==========================================================================
# Python - Security Scanning
# ==========================================================================
- repo: https://github.com/PyCQA/bandit
rev: 1.9.2
hooks:
- id: bandit
args: [-c, pyproject.toml, -r, src, macros]
additional_dependencies: ["bandit[toml]"]
types: [text]
files: \.(py|FCMacro)$
# Safety - Dependency vulnerability scanning
# Checks installed packages against known security vulnerabilities
# Local: Requires free safetycli.com account. Run `uv run safety auth` first.
# CI: Uses SAFETY_API_KEY secret passed via environment variable.
# Config: .safety-policy.yml (excludes .venv, node_modules, etc.)
- repo: local
hooks:
- id: safety
name: safety (dependency vulnerabilities)
entry: uv run safety scan --policy-file .safety-policy.yml --detailed-output
language: system
pass_filenames: false
files: ^(pyproject\.toml|uv\.lock|\.safety-policy\.yml)$
# ==========================================================================
# Secrets Detection - Multi-Layer Approach
# ==========================================================================
# Layer 1: Gitleaks - Fast, comprehensive secrets scanner
# Scans git history and current files using regex patterns
# Config: .gitleaks.toml
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.0
hooks:
- id: gitleaks
name: gitleaks (secrets scanner)
args: [--config, .gitleaks.toml, --verbose]
# Layer 2: detect-secrets - Yelp's enterprise-grade secrets detector
# Uses baseline file to track known/approved secrets
# Config: .secrets.baseline
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
- id: detect-secrets
name: detect-secrets (baseline scan)
args:
- --baseline
- .secrets.baseline
- --exclude-files
- '\.secrets\.baseline$'
- --exclude-files
- '\.gitleaks\.toml$'
- --exclude-files
- 'uv\.lock$'
- --exclude-files
- 'poetry\.lock$'
- --exclude-files
- 'package-lock\.json$'
# Layer 3: TruffleHog - Deep secrets scanner with verification
# Verifies secrets are actually valid (e.g., tests AWS keys)
# Note: TruffleHog has wasm/go-re2 panic bugs in GitHub Actions.
# It's skipped in CI (via SKIP env var) but runs locally.
# See: https://github.com/trufflesecurity/trufflehog/issues/3321
- repo: https://github.com/trufflesecurity/trufflehog
rev: v3.92.4
hooks:
- id: trufflehog
name: trufflehog (verified secrets scan)
args:
- --no-update
exclude: '(^|/)uv\.lock$|\.secrets\.baseline$'
# ==========================================================================
# Markdown Linting
# ==========================================================================
# markdownlint-cli2 - Comprehensive markdown linter with auto-fix
# Config: .markdownlint.yaml
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.20.0
hooks:
- id: markdownlint-cli2
name: markdownlint (linter)
args: [--fix]
# Tertiary: md-toc - Table of contents generator
# Automatically updates TOC between <!--TOC--> markers
- repo: https://github.com/frnmst/md-toc
rev: 9.0.0
hooks:
- id: md-toc
name: md-toc (table of contents)
args: ["-p", "github", "-l", "6"] # GitHub parser, max 6 levels
files: ^(README|docs/development/architecture-detailed)\.md$
# ==========================================================================
# Spell Checking
# ==========================================================================
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
additional_dependencies:
- tomli
args:
- --ignore-words
- .codespell-ignore-words.txt
- --skip
- "*.lock,*.json,.secrets.baseline"
# ==========================================================================
# Configuration Validation
# ==========================================================================
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.24.1
hooks:
- id: validate-pyproject
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.36.0
hooks:
- id: check-github-workflows
name: validate GitHub workflows
- id: check-dependabot
name: validate Dependabot config
# ==========================================================================
# GitHub Actions Linting
# ==========================================================================
- repo: https://github.com/rhysd/actionlint
rev: v1.7.10
hooks:
- id: actionlint
name: actionlint (GitHub Actions linter)
# ==========================================================================
# Shell Script Linting
# ==========================================================================
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
hooks:
- id: shellcheck
name: shellcheck (shell linter)
args: [--severity=warning]
# ==========================================================================
# Dockerfile Linting
# ==========================================================================
# hadolint-py: Python wrapper that auto-downloads hadolint binary
# No Docker or system installation required
- repo: https://github.com/AleksaC/hadolint-py
rev: v2.14.0
hooks:
- id: hadolint
# ==========================================================================
# Dockerfile Security Scanning (Misconfigurations)
# ==========================================================================
# Uses mise-managed trivy binary instead of pre-commit repo.
# This avoids case-conflicting git refs in pre-commit-trivy repo that break
# `pre-commit autoupdate` on case-insensitive filesystems (macOS).
# Version is managed in .mise.toml - update with `mise upgrade trivy`
- repo: local
hooks:
- id: trivy
name: trivy (Dockerfile misconfig)
entry: trivy
args:
- config
- --severity
- HIGH,CRITICAL
- --exit-code
- "1"
language: system
files: (Dockerfile|\.dockerfile)$
pass_filenames: true
# ==========================================================================
# Documentation Build Validation
# ==========================================================================
- repo: local
hooks:
- id: mkdocs-build
name: mkdocs (documentation build)
entry: uv run mkdocs build --strict
language: system
pass_filenames: false
files: ^(docs/|mkdocs\.yaml)
# ==========================================================================
# Commit Message Linting
# ==========================================================================
- repo: https://github.com/commitizen-tools/commitizen
rev: v4.11.1
hooks:
- id: commitizen
name: commitizen (commit format)
stages: [commit-msg]
# ==========================================================================
# AI Code Review (Local Only)
# ==========================================================================
# CodeRabbit CLI - AI-powered code review
# https://www.coderabbit.ai/cli
#
# SETUP REQUIRED:
# 1. Install: just coderabbit::install
# 2. Authenticate: just coderabbit::login
#
# USAGE:
# - Run manually: just coderabbit::review
# - Run via pre-commit: uv run pre-commit run coderabbit --all-files
#
# NOTE: This hook uses 'manual' stage so it doesn't run automatically.
# The CodeRabbit GitHub App already reviews PRs, so CLI is for local use.
# Rate limits: Free=1/hour, Lite=1/hour, Pro=5/hour
- repo: local
hooks:
- id: coderabbit
name: coderabbit (AI code review)
entry: coderabbit review --plain --type uncommitted
language: system
pass_filenames: false
stages: [manual]
verbose: true
# ==========================================================================
# CI Configuration
# ==========================================================================
ci:
autoupdate_schedule: monthly
autoupdate_commit_msg: "chore(deps): update pre-commit hooks"
skip:
- mypy # Needs dependencies installed
- trivy # Uses mise-managed binary (local repo)
- trufflehog # Can be slow in CI
- coderabbit # GitHub App handles PR reviews; CLI is for local use