chrore: major overhaul of docs, commands, and workflows (#23)
* ci: add status badges to README * chore: major overhaul - docs, commands & workflows * fix: general fixes and improvements * chore: various updates and fixes * chore: minor fixes
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# Development utility commands
|
||||
# Usage: just dev::clean, just dev::repl, etc.
|
||||
#
|
||||
# Miscellaneous development tools and utilities.
|
||||
|
||||
# Project root directory (justfile_directory() returns the main justfile's directory)
|
||||
project_root := justfile_directory()
|
||||
|
||||
# =============================================================================
|
||||
# Setup & Dependencies
|
||||
# =============================================================================
|
||||
|
||||
# Install all project dependencies (Python packages + dev tools)
|
||||
install-deps:
|
||||
cd {{project_root}} && uv sync --all-extras
|
||||
@echo ""
|
||||
@echo "Dependencies installed!"
|
||||
@echo ""
|
||||
@echo "To run the MCP server:"
|
||||
@echo " just mcp::run # stdio mode"
|
||||
@echo " just mcp::run-debug # with debug logging"
|
||||
@echo " just mcp::run-http # HTTP mode for remote access"
|
||||
|
||||
# Install pre-commit hooks (for git commit/push integration)
|
||||
install-pre-commit:
|
||||
cd {{project_root}} && uv run pre-commit install
|
||||
cd {{project_root}} && uv run pre-commit install --hook-type commit-msg
|
||||
|
||||
# Update all dependencies to latest versions (uv.lock + pre-commit hooks)
|
||||
update-deps:
|
||||
cd {{project_root}} && uv lock --upgrade
|
||||
cd {{project_root}} && uv sync --all-extras
|
||||
cd {{project_root}} && uv run pre-commit autoupdate
|
||||
|
||||
# =============================================================================
|
||||
# Development Utilities
|
||||
# =============================================================================
|
||||
|
||||
# Clean build artifacts and caches
|
||||
clean:
|
||||
rm -rf {{project_root}}/.pytest_cache {{project_root}}/.mypy_cache {{project_root}}/.ruff_cache {{project_root}}/.coverage {{project_root}}/htmlcov {{project_root}}/dist {{project_root}}/build
|
||||
find {{project_root}} -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
||||
find {{project_root}} -type f -name "*.pyc" -delete 2>/dev/null || true
|
||||
|
||||
# Open Python REPL with project modules available
|
||||
repl:
|
||||
cd {{project_root}} && uv run python -c "import freecad_mcp; print('FreeCAD MCP loaded')" && uv run python
|
||||
|
||||
# Show project structure (tree view, requires 'tree' command)
|
||||
tree:
|
||||
#!/usr/bin/env bash
|
||||
if ! command -v tree &> /dev/null; then
|
||||
echo "The 'tree' command is not installed."
|
||||
echo "Install it with: brew install tree (macOS) or apt install tree (Linux)"
|
||||
exit 1
|
||||
fi
|
||||
cd "{{project_root}}" && tree -I '__pycache__|*.egg-info|.git|.mypy_cache|.pytest_cache|.ruff_cache|htmlcov|dist|build|.venv|site' -a
|
||||
|
||||
# Validate project configuration files (pyproject.toml, etc.)
|
||||
validate:
|
||||
cd {{project_root}} && uv pip check
|
||||
@echo "Package dependencies are valid."
|
||||
@@ -67,6 +67,13 @@ clean:
|
||||
docker rmi {{image_name}} 2>/dev/null || true
|
||||
docker rmi {{registry}}/{{image_name}} 2>/dev/null || true
|
||||
|
||||
# Remove Docker images and build cache
|
||||
clean-all:
|
||||
docker rmi {{image_name}} 2>/dev/null || true
|
||||
docker rmi {{registry}}/{{image_name}} 2>/dev/null || true
|
||||
docker builder prune -f
|
||||
@echo "Docker images and build cache cleaned."
|
||||
|
||||
# Scan Docker image for vulnerabilities (warn on all severities)
|
||||
scan:
|
||||
#!/usr/bin/env bash
|
||||
|
||||
+14
-9
@@ -1,25 +1,30 @@
|
||||
# Documentation commands
|
||||
# Usage: just documentation::build, just documentation::serve, etc.
|
||||
|
||||
# Project root directory (justfile_directory() returns the main justfile's directory)
|
||||
project_root := justfile_directory()
|
||||
|
||||
# Build documentation
|
||||
build:
|
||||
uv run mkdocs build
|
||||
cd {{project_root}} && uv run mkdocs build
|
||||
|
||||
# Build documentation with strict mode (fails on warnings, for CI)
|
||||
build-strict:
|
||||
uv run mkdocs build --strict
|
||||
cd {{project_root}} && uv run mkdocs build --strict
|
||||
|
||||
# Serve documentation locally
|
||||
serve:
|
||||
uv run mkdocs serve
|
||||
cd {{project_root}} && uv run mkdocs serve
|
||||
|
||||
# Build and open documentation in browser
|
||||
open:
|
||||
#!/usr/bin/env bash
|
||||
cd "{{project_root}}"
|
||||
uv run mkdocs build
|
||||
@if [[ "$OSTYPE" == "darwin"* ]]; then \
|
||||
open site/index.html; \
|
||||
elif command -v xdg-open &> /dev/null; then \
|
||||
xdg-open site/index.html; \
|
||||
else \
|
||||
echo "Documentation built at: site/index.html"; \
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
open "{{project_root}}/site/index.html"
|
||||
elif command -v xdg-open &> /dev/null; then
|
||||
xdg-open "{{project_root}}/site/index.html"
|
||||
else
|
||||
echo "Documentation built at: {{project_root}}/site/index.html"
|
||||
fi
|
||||
|
||||
+8
-251
@@ -252,255 +252,12 @@ run-gui-custom freecad_path:
|
||||
echo "FreeCAD is starting with MCP bridge..."
|
||||
|
||||
# =============================================================================
|
||||
# Macro Installation (MultiExport, CutObjectForMagnets)
|
||||
# Deprecated Aliases (use install:: module instead)
|
||||
# =============================================================================
|
||||
|
||||
# Install the CutObjectForMagnets macro to FreeCAD's macro directory
|
||||
install-cut-macro:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
PROJECT_DIR="{{project_root}}"
|
||||
|
||||
# Determine macro directory based on OS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MACRO_DIR="$HOME/Library/Application Support/FreeCAD/Macro"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MACRO_DIR="$HOME/.local/share/FreeCAD/Macro"
|
||||
else
|
||||
MACRO_DIR="$APPDATA/FreeCAD/Macro"
|
||||
fi
|
||||
|
||||
mkdir -p "$MACRO_DIR"
|
||||
|
||||
# Copy the macro file
|
||||
cp "${PROJECT_DIR}/macros/Cut_Object_for_Magnets/CutObjectForMagnets.FCMacro" "$MACRO_DIR/"
|
||||
|
||||
# Optionally copy the icon if it exists
|
||||
if [[ -f "${PROJECT_DIR}/macros/Cut_Object_for_Magnets/CutObjectForMagnets.svg" ]]; then
|
||||
cp "${PROJECT_DIR}/macros/Cut_Object_for_Magnets/CutObjectForMagnets.svg" "$MACRO_DIR/"
|
||||
fi
|
||||
|
||||
echo "CutObjectForMagnets macro installed to: $MACRO_DIR"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo " 1. Start FreeCAD"
|
||||
echo " 2. Select an object to cut"
|
||||
echo " 3. Go to: Macro → Macros → CutObjectForMagnets → Execute"
|
||||
echo ""
|
||||
echo "For angled cuts:"
|
||||
echo " 1. Create a datum plane at your desired angle (Part Design → Create datum plane)"
|
||||
echo " 2. Select 'Model Plane' in the macro dialog"
|
||||
echo " 3. Choose your datum plane from the dropdown"
|
||||
|
||||
# Uninstall the CutObjectForMagnets macro
|
||||
uninstall-cut-macro:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MACRO_DIR="$HOME/Library/Application Support/FreeCAD/Macro"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MACRO_DIR="$HOME/.local/share/FreeCAD/Macro"
|
||||
else
|
||||
MACRO_DIR="$APPDATA/FreeCAD/Macro"
|
||||
fi
|
||||
rm -f "$MACRO_DIR/CutObjectForMagnets.FCMacro"
|
||||
rm -f "$MACRO_DIR/CutObjectForMagnets.svg"
|
||||
echo "CutObjectForMagnets macro uninstalled"
|
||||
|
||||
# Install the MultiExport macro to FreeCAD's macro directory
|
||||
install-export-macro:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
PROJECT_DIR="{{project_root}}"
|
||||
|
||||
# Determine macro directory based on OS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MACRO_DIR="$HOME/Library/Application Support/FreeCAD/Macro"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MACRO_DIR="$HOME/.local/share/FreeCAD/Macro"
|
||||
else
|
||||
MACRO_DIR="$APPDATA/FreeCAD/Macro"
|
||||
fi
|
||||
|
||||
mkdir -p "$MACRO_DIR"
|
||||
|
||||
# Copy the macro file
|
||||
cp "${PROJECT_DIR}/macros/Multi_Export/MultiExport.FCMacro" "$MACRO_DIR/"
|
||||
|
||||
# Optionally copy the icon if it exists
|
||||
if [[ -f "${PROJECT_DIR}/macros/Multi_Export/MultiExport.svg" ]]; then
|
||||
cp "${PROJECT_DIR}/macros/Multi_Export/MultiExport.svg" "$MACRO_DIR/"
|
||||
fi
|
||||
|
||||
echo "MultiExport macro installed to: $MACRO_DIR"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo " 1. Start FreeCAD"
|
||||
echo " 2. Select one or more objects to export"
|
||||
echo " 3. Go to: Macro → Macros → MultiExport → Execute"
|
||||
echo " 4. Choose export formats (STL, STEP, 3MF selected by default)"
|
||||
echo " 5. Set output directory and filename"
|
||||
echo " 6. Click Export"
|
||||
|
||||
# Uninstall the MultiExport macro
|
||||
uninstall-export-macro:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MACRO_DIR="$HOME/Library/Application Support/FreeCAD/Macro"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MACRO_DIR="$HOME/.local/share/FreeCAD/Macro"
|
||||
else
|
||||
MACRO_DIR="$APPDATA/FreeCAD/Macro"
|
||||
fi
|
||||
rm -f "$MACRO_DIR/MultiExport.FCMacro"
|
||||
rm -f "$MACRO_DIR/MultiExport.svg"
|
||||
echo "MultiExport macro uninstalled"
|
||||
|
||||
# Install all macros to FreeCAD's macro directory
|
||||
install-all-macros: install-cut-macro install-export-macro
|
||||
@echo "All macros installed successfully!"
|
||||
|
||||
# Uninstall all macros from FreeCAD's macro directory
|
||||
uninstall-all-macros: uninstall-cut-macro uninstall-export-macro
|
||||
@echo "All macros uninstalled successfully!"
|
||||
|
||||
# =============================================================================
|
||||
# Workbench Addon Installation
|
||||
# =============================================================================
|
||||
|
||||
# Install the FreeCAD Robust MCP workbench addon to FreeCAD's Mod directory
|
||||
install-workbench:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
PROJECT_DIR="{{project_root}}"
|
||||
ADDON_NAME="FreecadRobustMCP"
|
||||
|
||||
# Determine FreeCAD Mod directory based on OS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MOD_DIR="$HOME/Library/Application Support/FreeCAD/Mod"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MOD_DIR="$HOME/.local/share/FreeCAD/Mod"
|
||||
else
|
||||
MOD_DIR="$APPDATA/FreeCAD/Mod"
|
||||
fi
|
||||
|
||||
ADDON_DEST="$MOD_DIR/$ADDON_NAME"
|
||||
|
||||
# Create Mod directory if it doesn't exist
|
||||
mkdir -p "$MOD_DIR"
|
||||
|
||||
# Remove existing installation if present
|
||||
if [[ -d "$ADDON_DEST" ]]; then
|
||||
echo "Removing existing installation at: $ADDON_DEST"
|
||||
rm -rf "$ADDON_DEST"
|
||||
fi
|
||||
|
||||
# Copy the addon directory
|
||||
cp -r "${PROJECT_DIR}/addon/$ADDON_NAME" "$ADDON_DEST"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "FreeCAD Robust MCP Workbench installed!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Installation path: $ADDON_DEST"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo " 1. Start FreeCAD"
|
||||
echo " 2. Select the 'MCP Bridge' workbench from the workbench selector"
|
||||
echo " 3. Click 'Start MCP Bridge' in the toolbar"
|
||||
echo " 4. Connect your MCP client (Claude Code, etc.) to FreeCAD"
|
||||
echo ""
|
||||
|
||||
# Uninstall the FreeCAD Robust MCP workbench addon
|
||||
uninstall-workbench:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ADDON_NAME="FreecadRobustMCP"
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MOD_DIR="$HOME/Library/Application Support/FreeCAD/Mod"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MOD_DIR="$HOME/.local/share/FreeCAD/Mod"
|
||||
else
|
||||
MOD_DIR="$APPDATA/FreeCAD/Mod"
|
||||
fi
|
||||
|
||||
ADDON_DEST="$MOD_DIR/$ADDON_NAME"
|
||||
|
||||
if [[ -d "$ADDON_DEST" ]]; then
|
||||
rm -rf "$ADDON_DEST"
|
||||
echo "FreeCAD Robust MCP Workbench uninstalled from: $ADDON_DEST"
|
||||
else
|
||||
echo "Workbench not found at: $ADDON_DEST"
|
||||
fi
|
||||
|
||||
# Check workbench installation status
|
||||
mcp-status:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Determine directories based on OS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MOD_DIR="$HOME/Library/Application Support/FreeCAD/Mod"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MOD_DIR="$HOME/.local/share/FreeCAD/Mod"
|
||||
else
|
||||
MOD_DIR="$APPDATA/FreeCAD/Mod"
|
||||
fi
|
||||
|
||||
echo "=========================================="
|
||||
echo "MCP Bridge Installation Status"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check workbench
|
||||
if [[ -d "$MOD_DIR/FreecadRobustMCP" ]]; then
|
||||
echo "✓ Workbench addon: INSTALLED"
|
||||
echo " Path: $MOD_DIR/FreecadRobustMCP"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo " 1. Start FreeCAD"
|
||||
echo " 2. Select 'MCP Bridge' workbench"
|
||||
echo " 3. Click 'Start MCP Bridge' in toolbar"
|
||||
else
|
||||
echo "✗ Workbench addon: NOT INSTALLED"
|
||||
echo ""
|
||||
echo "To install:"
|
||||
echo " just freecad::install-workbench"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check for legacy installations that should be cleaned up
|
||||
LEGACY_COUNT=0
|
||||
|
||||
if [[ -d "$MOD_DIR/MCPBridge" ]]; then
|
||||
echo "⚠ Legacy plugin found: $MOD_DIR/MCPBridge"
|
||||
echo " Run: rm -rf \"$MOD_DIR/MCPBridge\""
|
||||
((LEGACY_COUNT++)) || true
|
||||
fi
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MACRO_DIR="$HOME/Library/Application Support/FreeCAD/Macro"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MACRO_DIR="$HOME/.local/share/FreeCAD/Macro"
|
||||
else
|
||||
MACRO_DIR="$APPDATA/FreeCAD/Macro"
|
||||
fi
|
||||
|
||||
if [[ -f "$MACRO_DIR/StartMCPBridge.FCMacro" ]]; then
|
||||
echo "⚠ Legacy macro found: $MACRO_DIR/StartMCPBridge.FCMacro"
|
||||
echo " Run: rm \"$MACRO_DIR/StartMCPBridge.FCMacro\""
|
||||
((LEGACY_COUNT++)) || true
|
||||
fi
|
||||
|
||||
if [[ $LEGACY_COUNT -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "Note: Legacy installations can be removed. The workbench replaces them."
|
||||
fi
|
||||
|
||||
echo "=========================================="
|
||||
|
||||
# Check if the workbench addon is installed (alias for mcp-status)
|
||||
workbench-status: mcp-status
|
||||
# These are kept for backwards compatibility but will be removed in a future version.
|
||||
# Use the new install:: module commands instead:
|
||||
# just install::macro-cut (was: just freecad::install-cut-macro)
|
||||
# just install::macro-export (was: just freecad::install-export-macro)
|
||||
# just install::macro-all (was: just freecad::install-all-macros)
|
||||
# just install::mcp-bridge-workbench (was: just freecad::install-workbench)
|
||||
# just install::status (was: just freecad::mcp-status)
|
||||
|
||||
@@ -0,0 +1,357 @@
|
||||
# Installation commands for users
|
||||
# Usage: just install::mcp-server, just install::mcp-bridge-workbench, etc.
|
||||
#
|
||||
# This module installs components for end users:
|
||||
# - MCP Server (as a uv tool, available system-wide)
|
||||
# - MCP Bridge Workbench (FreeCAD addon)
|
||||
# - FreeCAD Macros (CutObjectForMagnets, MultiExport)
|
||||
#
|
||||
# For developer setup (Python dependencies in virtualenv), use: just dev::install-deps
|
||||
|
||||
# Project root directory (justfile_directory() returns the main justfile's directory)
|
||||
project_root := justfile_directory()
|
||||
|
||||
# =============================================================================
|
||||
# Helper: FreeCAD Directory Detection
|
||||
# =============================================================================
|
||||
# This function sets MOD_DIR and MACRO_DIR based on the current OS.
|
||||
# Source it at the start of any recipe that needs FreeCAD paths.
|
||||
#
|
||||
# Usage in recipes:
|
||||
# eval "$(just install::_freecad-dirs)"
|
||||
# echo "Mod directory: $MOD_DIR"
|
||||
# echo "Macro directory: $MACRO_DIR"
|
||||
|
||||
# Private recipe that outputs shell code to set FreeCAD directories
|
||||
[private]
|
||||
_freecad-dirs:
|
||||
#!/usr/bin/env bash
|
||||
cat << 'DIRS_EOF'
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
MOD_DIR="$HOME/Library/Application Support/FreeCAD/Mod"
|
||||
MACRO_DIR="$HOME/Library/Application Support/FreeCAD/Macro"
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
MOD_DIR="$HOME/.local/share/FreeCAD/Mod"
|
||||
MACRO_DIR="$HOME/.local/share/FreeCAD/Macro"
|
||||
else
|
||||
# Windows: validate APPDATA or use fallback
|
||||
if [[ -n "$APPDATA" ]]; then
|
||||
FREECAD_BASE="$APPDATA"
|
||||
elif [[ -n "$HOME" ]]; then
|
||||
# Fallback to standard Windows location under HOME
|
||||
FREECAD_BASE="$HOME/AppData/Roaming"
|
||||
echo "Warning: APPDATA not set, using fallback: $FREECAD_BASE" >&2
|
||||
else
|
||||
echo "Error: Neither APPDATA nor HOME is set. Cannot determine FreeCAD directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
MOD_DIR="$FREECAD_BASE/FreeCAD/Mod"
|
||||
MACRO_DIR="$FREECAD_BASE/FreeCAD/Macro"
|
||||
fi
|
||||
DIRS_EOF
|
||||
|
||||
# =============================================================================
|
||||
# MCP Server Installation
|
||||
# =============================================================================
|
||||
|
||||
# Install the MCP server as a user tool (available system-wide via uv)
|
||||
mcp-server:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
PROJECT_DIR="{{project_root}}"
|
||||
|
||||
echo "Installing MCP server as a uv tool..."
|
||||
echo ""
|
||||
|
||||
# Install from the local project directory
|
||||
uv tool install --force "$PROJECT_DIR"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "MCP Server installed!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "The 'freecad-mcp' command is now available system-wide."
|
||||
echo ""
|
||||
echo "To run:"
|
||||
echo " freecad-mcp # stdio mode (for Claude Code)"
|
||||
echo " freecad-mcp --help # show all options"
|
||||
echo ""
|
||||
echo "To configure Claude Code, add to your MCP settings:"
|
||||
echo ' "freecad": {'
|
||||
echo ' "command": "freecad-mcp"'
|
||||
echo ' }'
|
||||
echo ""
|
||||
|
||||
# Uninstall the MCP server tool
|
||||
uninstall-mcp-server:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
echo "Uninstalling MCP server..."
|
||||
uv tool uninstall freecad-robust-mcp || echo "MCP server was not installed as a uv tool"
|
||||
|
||||
# =============================================================================
|
||||
# MCP Bridge Workbench Installation
|
||||
# =============================================================================
|
||||
|
||||
# Install the FreeCAD Robust MCP workbench addon to FreeCAD's Mod directory
|
||||
mcp-bridge-workbench:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
PROJECT_DIR="{{project_root}}"
|
||||
ADDON_NAME="FreecadRobustMCP"
|
||||
|
||||
# Set FreeCAD directories
|
||||
eval "$(just install::_freecad-dirs)"
|
||||
|
||||
ADDON_DEST="$MOD_DIR/$ADDON_NAME"
|
||||
|
||||
# Create Mod directory if it doesn't exist
|
||||
mkdir -p "$MOD_DIR"
|
||||
|
||||
# Remove existing installation if present
|
||||
if [[ -d "$ADDON_DEST" ]]; then
|
||||
echo "Removing existing installation at: $ADDON_DEST"
|
||||
rm -rf "$ADDON_DEST"
|
||||
fi
|
||||
|
||||
# Verify source exists before copying
|
||||
ADDON_SRC="${PROJECT_DIR}/addon/$ADDON_NAME"
|
||||
if [[ ! -d "$ADDON_SRC" ]]; then
|
||||
echo "Error: Addon source directory not found: $ADDON_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy the addon directory
|
||||
cp -r "$ADDON_SRC" "$ADDON_DEST"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "FreeCAD Robust MCP Workbench installed!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Installation path: $ADDON_DEST"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo " 1. Start FreeCAD"
|
||||
echo " 2. Select the 'MCP Bridge' workbench from the workbench selector"
|
||||
echo " 3. Click 'Start MCP Bridge' in the toolbar"
|
||||
echo " 4. Connect your MCP client (Claude Code, etc.) to FreeCAD"
|
||||
echo ""
|
||||
|
||||
# Uninstall the FreeCAD Robust MCP workbench addon
|
||||
uninstall-mcp-bridge-workbench:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ADDON_NAME="FreecadRobustMCP"
|
||||
|
||||
# Set FreeCAD directories
|
||||
eval "$(just install::_freecad-dirs)"
|
||||
|
||||
ADDON_DEST="$MOD_DIR/$ADDON_NAME"
|
||||
|
||||
if [[ -d "$ADDON_DEST" ]]; then
|
||||
rm -rf "$ADDON_DEST"
|
||||
echo "FreeCAD Robust MCP Workbench uninstalled from: $ADDON_DEST"
|
||||
else
|
||||
echo "Workbench not found at: $ADDON_DEST"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Macro Installation
|
||||
# =============================================================================
|
||||
|
||||
# Install the CutObjectForMagnets macro to FreeCAD's macro directory
|
||||
macro-cut:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
PROJECT_DIR="{{project_root}}"
|
||||
|
||||
# Set FreeCAD directories
|
||||
eval "$(just install::_freecad-dirs)"
|
||||
|
||||
# Verify source exists before copying
|
||||
MACRO_SRC="${PROJECT_DIR}/macros/Cut_Object_for_Magnets/CutObjectForMagnets.FCMacro"
|
||||
if [[ ! -f "$MACRO_SRC" ]]; then
|
||||
echo "Error: Macro source file not found: $MACRO_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$MACRO_DIR"
|
||||
|
||||
# Copy the macro file
|
||||
cp "$MACRO_SRC" "$MACRO_DIR/"
|
||||
|
||||
# Optionally copy the icon if it exists
|
||||
ICON_SRC="${PROJECT_DIR}/macros/Cut_Object_for_Magnets/CutObjectForMagnets.svg"
|
||||
if [[ -f "$ICON_SRC" ]]; then
|
||||
cp "$ICON_SRC" "$MACRO_DIR/"
|
||||
fi
|
||||
|
||||
echo "CutObjectForMagnets macro installed to: $MACRO_DIR"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo " 1. Start FreeCAD"
|
||||
echo " 2. Select an object to cut"
|
||||
echo " 3. Go to: Macro → Macros → CutObjectForMagnets → Execute"
|
||||
echo ""
|
||||
echo "For angled cuts:"
|
||||
echo " 1. Create a datum plane at your desired angle (Part Design → Create datum plane)"
|
||||
echo " 2. Select 'Model Plane' in the macro dialog"
|
||||
echo " 3. Choose your datum plane from the dropdown"
|
||||
|
||||
# Uninstall the CutObjectForMagnets macro
|
||||
uninstall-macro-cut:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Set FreeCAD directories
|
||||
eval "$(just install::_freecad-dirs)"
|
||||
|
||||
rm -f "$MACRO_DIR/CutObjectForMagnets.FCMacro"
|
||||
rm -f "$MACRO_DIR/CutObjectForMagnets.svg"
|
||||
echo "CutObjectForMagnets macro uninstalled"
|
||||
|
||||
# Install the MultiExport macro to FreeCAD's macro directory
|
||||
macro-export:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
PROJECT_DIR="{{project_root}}"
|
||||
|
||||
# Set FreeCAD directories
|
||||
eval "$(just install::_freecad-dirs)"
|
||||
|
||||
# Verify source exists before copying
|
||||
MACRO_SRC="${PROJECT_DIR}/macros/Multi_Export/MultiExport.FCMacro"
|
||||
if [[ ! -f "$MACRO_SRC" ]]; then
|
||||
echo "Error: Macro source file not found: $MACRO_SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$MACRO_DIR"
|
||||
|
||||
# Copy the macro file
|
||||
cp "$MACRO_SRC" "$MACRO_DIR/"
|
||||
|
||||
# Optionally copy the icon if it exists
|
||||
ICON_SRC="${PROJECT_DIR}/macros/Multi_Export/MultiExport.svg"
|
||||
if [[ -f "$ICON_SRC" ]]; then
|
||||
cp "$ICON_SRC" "$MACRO_DIR/"
|
||||
fi
|
||||
|
||||
echo "MultiExport macro installed to: $MACRO_DIR"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo " 1. Start FreeCAD"
|
||||
echo " 2. Select one or more objects to export"
|
||||
echo " 3. Go to: Macro → Macros → MultiExport → Execute"
|
||||
echo " 4. Choose export formats (STL, STEP, 3MF selected by default)"
|
||||
echo " 5. Set output directory and filename"
|
||||
echo " 6. Click Export"
|
||||
|
||||
# Uninstall the MultiExport macro
|
||||
uninstall-macro-export:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Set FreeCAD directories
|
||||
eval "$(just install::_freecad-dirs)"
|
||||
|
||||
rm -f "$MACRO_DIR/MultiExport.FCMacro"
|
||||
rm -f "$MACRO_DIR/MultiExport.svg"
|
||||
echo "MultiExport macro uninstalled"
|
||||
|
||||
# Install all macros to FreeCAD's macro directory
|
||||
macro-all: macro-cut macro-export
|
||||
@echo "All macros installed successfully!"
|
||||
|
||||
# Uninstall all macros from FreeCAD's macro directory
|
||||
uninstall-macro-all: uninstall-macro-cut uninstall-macro-export
|
||||
@echo "All macros uninstalled successfully!"
|
||||
|
||||
# =============================================================================
|
||||
# Status Check
|
||||
# =============================================================================
|
||||
|
||||
# Check installation status of all components
|
||||
status:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Set FreeCAD directories
|
||||
eval "$(just install::_freecad-dirs)"
|
||||
|
||||
echo "=========================================="
|
||||
echo "Installation Status"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check MCP Server (installed as uv tool)
|
||||
if command -v freecad-mcp &> /dev/null; then
|
||||
echo "✓ MCP Server: INSTALLED (as uv tool)"
|
||||
echo " Run: freecad-mcp"
|
||||
elif [[ -f "{{project_root}}/pyproject.toml" ]] && grep -q 'name = "freecad-robust-mcp"' "{{project_root}}/pyproject.toml" 2>/dev/null; then
|
||||
# Dev environment exists - check if synced by looking for .venv
|
||||
if [[ -d "{{project_root}}/.venv" ]]; then
|
||||
echo "✓ MCP Server: AVAILABLE (via dev environment)"
|
||||
echo " Run: just mcp::run"
|
||||
echo " For system-wide install: just install::mcp-server"
|
||||
else
|
||||
echo "○ MCP Server: DEV SOURCE AVAILABLE (needs setup)"
|
||||
echo " Setup: uv sync --all-extras"
|
||||
echo " Then run: just mcp::run"
|
||||
fi
|
||||
else
|
||||
echo "✗ MCP Server: NOT INSTALLED"
|
||||
echo " Install: just install::mcp-server"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check workbench
|
||||
if [[ -d "$MOD_DIR/FreecadRobustMCP" ]]; then
|
||||
echo "✓ MCP Bridge Workbench: INSTALLED"
|
||||
echo " Path: $MOD_DIR/FreecadRobustMCP"
|
||||
else
|
||||
echo "✗ MCP Bridge Workbench: NOT INSTALLED"
|
||||
echo " Install: just install::mcp-bridge-workbench"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check macros
|
||||
echo "Macros:"
|
||||
if [[ -f "$MACRO_DIR/CutObjectForMagnets.FCMacro" ]]; then
|
||||
echo " ✓ CutObjectForMagnets: INSTALLED"
|
||||
else
|
||||
echo " ✗ CutObjectForMagnets: NOT INSTALLED"
|
||||
echo " Install: just install::macro-cut"
|
||||
fi
|
||||
|
||||
if [[ -f "$MACRO_DIR/MultiExport.FCMacro" ]]; then
|
||||
echo " ✓ MultiExport: INSTALLED"
|
||||
else
|
||||
echo " ✗ MultiExport: NOT INSTALLED"
|
||||
echo " Install: just install::macro-export"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check for legacy installations
|
||||
LEGACY_COUNT=0
|
||||
|
||||
if [[ -d "$MOD_DIR/MCPBridge" ]]; then
|
||||
echo "⚠ Legacy plugin found: $MOD_DIR/MCPBridge"
|
||||
echo " Run: rm -rf \"$MOD_DIR/MCPBridge\""
|
||||
((LEGACY_COUNT++)) || true
|
||||
fi
|
||||
|
||||
if [[ -f "$MACRO_DIR/StartMCPBridge.FCMacro" ]]; then
|
||||
echo "⚠ Legacy macro found: $MACRO_DIR/StartMCPBridge.FCMacro"
|
||||
echo " Run: rm \"$MACRO_DIR/StartMCPBridge.FCMacro\""
|
||||
((LEGACY_COUNT++)) || true
|
||||
fi
|
||||
|
||||
if [[ $LEGACY_COUNT -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "Note: Legacy installations can be removed. The workbench replaces them."
|
||||
fi
|
||||
|
||||
echo "=========================================="
|
||||
@@ -0,0 +1,18 @@
|
||||
# MCP Server commands
|
||||
# Usage: just mcp::run, just mcp::run-debug, etc.
|
||||
#
|
||||
# These commands run the MCP server that connects to FreeCAD.
|
||||
# Note: FreeCAD must be running with the MCP bridge for the server to connect.
|
||||
# Start FreeCAD with: just freecad::run-gui or just freecad::run-headless
|
||||
|
||||
# Run the MCP server (stdio mode - default for Claude Code integration)
|
||||
run:
|
||||
uv run freecad-mcp
|
||||
|
||||
# Run the MCP server with debug logging
|
||||
run-debug:
|
||||
FREECAD_MCP_LOG_LEVEL=DEBUG uv run freecad-mcp
|
||||
|
||||
# Run in HTTP mode for remote access (useful for testing or remote clients)
|
||||
run-http port="8000":
|
||||
FREECAD_MCP_TRANSPORT=http FREECAD_MCP_PORT={{port}} uv run freecad-mcp
|
||||
+24
-21
@@ -4,6 +4,9 @@
|
||||
# Note: Tools installed via mise (gitleaks, markdownlint-cli2, etc.) use `mise exec`.
|
||||
# Python tools use `uv run`. This ensures commands work even without mise shell activation.
|
||||
|
||||
# Project root directory (justfile_directory() returns the main justfile's directory)
|
||||
project_root := justfile_directory()
|
||||
|
||||
# Run all pre-commit checks
|
||||
check: _check-safety-auth
|
||||
uv run pre-commit run --all-files
|
||||
@@ -31,56 +34,56 @@ _check-safety-auth:
|
||||
|
||||
# Format code with ruff
|
||||
format:
|
||||
uv run ruff format src tests
|
||||
uv run ruff check --fix src tests
|
||||
uv run ruff format {{project_root}}/src {{project_root}}/tests
|
||||
uv run ruff check --fix {{project_root}}/src {{project_root}}/tests
|
||||
|
||||
# Run linting
|
||||
lint:
|
||||
uv run ruff check src tests
|
||||
uv run ruff check {{project_root}}/src {{project_root}}/tests
|
||||
|
||||
# Run type checking
|
||||
typecheck:
|
||||
uv run mypy src
|
||||
cd {{project_root}} && uv run mypy src
|
||||
|
||||
# Run security scanning (code vulnerabilities)
|
||||
security:
|
||||
uv run bandit -c pyproject.toml -r src
|
||||
uv run bandit -c {{project_root}}/pyproject.toml -r {{project_root}}/src
|
||||
uv run safety scan --detailed-output
|
||||
|
||||
# Run spell checking
|
||||
spellcheck:
|
||||
uv run codespell src tests docs
|
||||
uv run codespell {{project_root}}/src {{project_root}}/tests {{project_root}}/docs
|
||||
|
||||
# =============================================================================
|
||||
# Secrets Scanning
|
||||
# Secrets Scanning (quality::scan-* commands)
|
||||
# =============================================================================
|
||||
|
||||
# Run all secrets scanners
|
||||
secrets: secrets-gitleaks secrets-detect secrets-trufflehog # pragma: allowlist secret
|
||||
scan: scan-gitleaks scan-detect scan-trufflehog # pragma: allowlist secret
|
||||
@echo "All secrets scans complete!"
|
||||
|
||||
# Run gitleaks secrets scanner (installed via mise)
|
||||
secrets-gitleaks:
|
||||
mise exec -- gitleaks detect --config .gitleaks.toml --verbose
|
||||
scan-gitleaks:
|
||||
mise exec -- gitleaks detect --source {{project_root}} --config {{project_root}}/.gitleaks.toml --verbose
|
||||
|
||||
# Run gitleaks on git history
|
||||
secrets-gitleaks-history:
|
||||
mise exec -- gitleaks detect --config .gitleaks.toml --verbose --log-opts="--all"
|
||||
scan-gitleaks-history:
|
||||
mise exec -- gitleaks detect --source {{project_root}} --config {{project_root}}/.gitleaks.toml --verbose --log-opts="--all"
|
||||
|
||||
# Run detect-secrets scanner (installed via uv)
|
||||
secrets-detect:
|
||||
uv run detect-secrets scan --baseline .secrets.baseline
|
||||
scan-detect:
|
||||
uv run detect-secrets scan --baseline {{project_root}}/.secrets.baseline
|
||||
|
||||
# Audit detect-secrets baseline (interactive)
|
||||
secrets-audit:
|
||||
uv run detect-secrets audit .secrets.baseline
|
||||
scan-audit:
|
||||
uv run detect-secrets audit {{project_root}}/.secrets.baseline
|
||||
|
||||
# Update detect-secrets baseline with new findings
|
||||
secrets-baseline-update:
|
||||
uv run detect-secrets scan --baseline .secrets.baseline --update
|
||||
scan-baseline-update:
|
||||
uv run detect-secrets scan --baseline {{project_root}}/.secrets.baseline --update
|
||||
|
||||
# Run trufflehog for verified secrets (via pre-commit - not installed standalone)
|
||||
secrets-trufflehog:
|
||||
scan-trufflehog:
|
||||
uv run pre-commit run trufflehog --all-files
|
||||
|
||||
# =============================================================================
|
||||
@@ -89,8 +92,8 @@ secrets-trufflehog:
|
||||
|
||||
# Lint all markdown files (markdownlint-cli2 installed via mise)
|
||||
markdown-lint:
|
||||
mise exec -- markdownlint-cli2 "**/*.md"
|
||||
cd {{project_root}} && mise exec -- markdownlint-cli2 "**/*.md" "#.venv" "#.pytest_cache" "#node_modules" "#site" "#htmlcov"
|
||||
|
||||
# Lint and fix markdown files
|
||||
markdown-fix:
|
||||
mise exec -- markdownlint-cli2 --fix "**/*.md"
|
||||
cd {{project_root}} && mise exec -- markdownlint-cli2 --fix "**/*.md" "#.venv" "#.pytest_cache" "#node_modules" "#site" "#htmlcov"
|
||||
|
||||
@@ -0,0 +1,732 @@
|
||||
# Release commands for component-specific versioning
|
||||
# Usage: just release::bump-workbench 1.0.0, just release::tag-workbench 1.0.0, etc.
|
||||
#
|
||||
# Release Process (two steps):
|
||||
# 1. Bump version: just release::bump-<component> <version>
|
||||
# - Updates all version strings in source files
|
||||
# - Commit the changes: git add -A && git commit -m "chore: bump <component> to <version>"
|
||||
# 2. Create tag: just release::tag-<component> <version>
|
||||
# - Verifies versions match the tag
|
||||
# - Creates and pushes the git tag
|
||||
# - Tag triggers GitHub Actions workflow
|
||||
#
|
||||
# This project uses component-specific git tags for releases:
|
||||
# - robust-mcp-server-vX.Y.Z (triggers PyPI, Docker, GitHub release)
|
||||
# - robust-mcp-workbench-vX.Y.Z (triggers workbench archive release)
|
||||
# - macro-cut-object-for-magnets-vX.Y.Z
|
||||
# - macro-multi-export-vX.Y.Z
|
||||
#
|
||||
# Version Format (SemVer 2.0):
|
||||
# - X.Y.Z - Stable release
|
||||
# - X.Y.Z-alpha - Alpha (TestPyPI only for MCP server)
|
||||
# - X.Y.Z-alpha.N - Alpha with number
|
||||
# - X.Y.Z-beta - Beta
|
||||
# - X.Y.Z-beta.N - Beta with number
|
||||
# - X.Y.Z-rc.N - Release candidate
|
||||
|
||||
# Project root directory
|
||||
project_root := justfile_directory()
|
||||
|
||||
# =============================================================================
|
||||
# Version Bump Commands
|
||||
# =============================================================================
|
||||
|
||||
# Bump the MCP Bridge workbench version in all source files
|
||||
bump-workbench version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="{{version}}"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
# Validate version format (SemVer 2.0)
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "ERROR: Version '$VERSION' is not valid semver (X.Y.Z or X.Y.Z-prerelease)"
|
||||
echo "Examples: 1.0.0, 1.0.0-alpha, 1.0.0-beta.1, 1.0.0-rc.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Bumping MCP Bridge Workbench to version: $VERSION (date: $TODAY)"
|
||||
echo ""
|
||||
|
||||
# Update __version__ in the bridge module's __init__.py
|
||||
INIT_FILE="{{project_root}}/addon/FreecadRobustMCP/freecad_mcp_bridge/__init__.py"
|
||||
if [ -f "$INIT_FILE" ]; then
|
||||
sed 's/^__version__ = "[^"]*"/__version__ = "'"$VERSION"'"/' "$INIT_FILE" > "$INIT_FILE.tmp" && mv "$INIT_FILE.tmp" "$INIT_FILE"
|
||||
echo "Updated $INIT_FILE:"
|
||||
grep "__version__" "$INIT_FILE"
|
||||
else
|
||||
echo "ERROR: File not found: $INIT_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update the workbench version in package.xml
|
||||
PACKAGE_XML="{{project_root}}/package.xml"
|
||||
if [ -f "$PACKAGE_XML" ]; then
|
||||
# Use awk for precise XML editing within the workbench section
|
||||
awk -v version="$VERSION" -v date="$TODAY" '
|
||||
/<workbench>/ { in_workbench=1 }
|
||||
/<\/workbench>/ { in_workbench=0 }
|
||||
in_workbench && /<version>/ {
|
||||
gsub(/<version>[^<]*<\/version>/, "<version>" version "</version>")
|
||||
}
|
||||
in_workbench && /<date>/ {
|
||||
gsub(/<date>[^<]*<\/date>/, "<date>" date "</date>")
|
||||
}
|
||||
{ print }
|
||||
' "$PACKAGE_XML" > "$PACKAGE_XML.tmp" && mv "$PACKAGE_XML.tmp" "$PACKAGE_XML"
|
||||
echo ""
|
||||
echo "Updated $PACKAGE_XML (workbench section):"
|
||||
grep -A3 '<workbench>' "$PACKAGE_XML" | head -5
|
||||
else
|
||||
echo "ERROR: File not found: $PACKAGE_XML"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Version bump complete!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review changes: git diff"
|
||||
echo " 2. Commit: git add -A && git commit -m 'chore: bump workbench to $VERSION'"
|
||||
echo " 3. Tag and release: just release::tag-workbench $VERSION"
|
||||
|
||||
# Private helper recipe for bumping macro versions
|
||||
# Parameters: macro_dir, macro_name, macro_file_basename, readme_basename, tag_command, version
|
||||
[private]
|
||||
_bump-macro macro_dir macro_name macro_file_basename readme_basename tag_command version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="{{version}}"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
MACRO_DIR="{{project_root}}/macros/{{macro_dir}}"
|
||||
MACRO_NAME="{{macro_name}}"
|
||||
|
||||
# Validate version format (SemVer 2.0)
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "ERROR: Version '$VERSION' is not valid semver (X.Y.Z or X.Y.Z-prerelease)"
|
||||
echo "Examples: 1.0.0, 1.0.0-alpha, 1.0.0-beta.1, 1.0.0-rc.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Bumping $MACRO_NAME Macro to version: $VERSION (date: $TODAY)"
|
||||
echo ""
|
||||
|
||||
# Update __Version__ and __Date__ in .FCMacro file
|
||||
MACRO_FILE="$MACRO_DIR/{{macro_file_basename}}"
|
||||
if [ -f "$MACRO_FILE" ]; then
|
||||
sed "s/^__Version__ = [\"'].*[\"']/__Version__ = \"${VERSION}\"/" "$MACRO_FILE" > "$MACRO_FILE.tmp" && mv "$MACRO_FILE.tmp" "$MACRO_FILE"
|
||||
sed "s/^__Date__ = [\"'].*[\"']/__Date__ = \"${TODAY}\"/" "$MACRO_FILE" > "$MACRO_FILE.tmp" && mv "$MACRO_FILE.tmp" "$MACRO_FILE"
|
||||
echo "Updated $MACRO_FILE:"
|
||||
grep -E "^__(Version|Date)__" "$MACRO_FILE"
|
||||
else
|
||||
echo "ERROR: File not found: $MACRO_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update README version line
|
||||
README_FILE="$MACRO_DIR/{{readme_basename}}"
|
||||
if [ -f "$README_FILE" ]; then
|
||||
sed "s/^\*\*Version:\*\* .*/\*\*Version:\*\* ${VERSION}/" "$README_FILE" > "$README_FILE.tmp" && mv "$README_FILE.tmp" "$README_FILE"
|
||||
echo ""
|
||||
echo "Updated $README_FILE:"
|
||||
grep "Version:" "$README_FILE" | head -1
|
||||
else
|
||||
echo "WARNING: File not found: $README_FILE"
|
||||
fi
|
||||
|
||||
# Update wiki-source.txt version and date
|
||||
WIKI_FILE="$MACRO_DIR/wiki-source.txt"
|
||||
if [ -f "$WIKI_FILE" ]; then
|
||||
sed "s/|Version=.*/|Version=${VERSION}/" "$WIKI_FILE" > "$WIKI_FILE.tmp" && mv "$WIKI_FILE.tmp" "$WIKI_FILE"
|
||||
sed "s/|Date=.*/|Date=${TODAY}/" "$WIKI_FILE" > "$WIKI_FILE.tmp" && mv "$WIKI_FILE.tmp" "$WIKI_FILE"
|
||||
echo ""
|
||||
echo "Updated $WIKI_FILE:"
|
||||
grep -E "^\|Version=|\|Date=" "$WIKI_FILE"
|
||||
else
|
||||
echo "WARNING: File not found: $WIKI_FILE"
|
||||
fi
|
||||
|
||||
# Update package.xml macro version
|
||||
PACKAGE_XML="{{project_root}}/package.xml"
|
||||
if [ -f "$PACKAGE_XML" ]; then
|
||||
awk -v name="$MACRO_NAME" -v version="$VERSION" -v date="$TODAY" '
|
||||
/<macro>/ { in_macro=1 }
|
||||
/<\/macro>/ { in_macro=0; found_name=0 }
|
||||
in_macro && /<name>.*<\/name>/ {
|
||||
if (index($0, name) > 0) found_name=1
|
||||
}
|
||||
in_macro && found_name && /<version>/ {
|
||||
gsub(/<version>[^<]*<\/version>/, "<version>" version "</version>")
|
||||
}
|
||||
in_macro && found_name && /<date>/ {
|
||||
gsub(/<date>[^<]*<\/date>/, "<date>" date "</date>")
|
||||
}
|
||||
{ print }
|
||||
' "$PACKAGE_XML" > "$PACKAGE_XML.tmp" && mv "$PACKAGE_XML.tmp" "$PACKAGE_XML"
|
||||
echo ""
|
||||
echo "Updated $PACKAGE_XML ($MACRO_NAME section):"
|
||||
grep -A4 ">$MACRO_NAME<" "$PACKAGE_XML"
|
||||
else
|
||||
echo "ERROR: File not found: $PACKAGE_XML"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Version bump complete!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review changes: git diff"
|
||||
echo " 2. Commit: git add -A && git commit -m 'chore: bump $MACRO_NAME macro to $VERSION'"
|
||||
echo " 3. Tag and release: just release::{{tag_command}} $VERSION"
|
||||
|
||||
# Bump the Cut Object for Magnets macro version in all source files
|
||||
bump-macro-magnets version: (_bump-macro "Cut_Object_for_Magnets" "Cut Object for Magnets" "CutObjectForMagnets.FCMacro" "README-CutObjectForMagnets.md" "tag-macro-magnets" version)
|
||||
|
||||
# Bump the Multi Export macro version in all source files
|
||||
bump-macro-export version: (_bump-macro "Multi_Export" "Multi Export" "MultiExport.FCMacro" "README-MultiExport.md" "tag-macro-export" version)
|
||||
|
||||
# =============================================================================
|
||||
# Tag Creation Commands
|
||||
# =============================================================================
|
||||
|
||||
# Create and push a release tag for the MCP server (triggers PyPI + Docker release)
|
||||
# Note: MCP server uses setuptools-scm, so version is derived from git tag at build time
|
||||
tag-mcp-server version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
TAG="robust-mcp-server-v{{version}}"
|
||||
|
||||
# Validate version format
|
||||
if [[ ! "{{version}}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "ERROR: Version '{{version}}' is not valid semver (X.Y.Z or X.Y.Z-prerelease)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for uncommitted changes
|
||||
if ! git diff --quiet HEAD; then
|
||||
echo "ERROR: You have uncommitted changes. Please commit or stash them first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating tag: $TAG"
|
||||
echo ""
|
||||
echo "This will trigger:"
|
||||
echo " - PyPI release (beta/rc/stable) or TestPyPI (alpha)"
|
||||
echo " - Docker Hub release"
|
||||
echo " - GitHub release with wheel and tar.gz"
|
||||
echo ""
|
||||
read -p "Continue? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git tag -a "$TAG" -m "Release MCP Server v{{version}}"
|
||||
git push origin "$TAG"
|
||||
echo ""
|
||||
echo "Tag $TAG created and pushed!"
|
||||
echo "Watch the release at: https://github.com/spkane/freecad-robust-mcp-and-more/actions"
|
||||
|
||||
# Create and push a release tag for the MCP Bridge workbench
|
||||
tag-workbench version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
TAG="robust-mcp-workbench-v{{version}}"
|
||||
VERSION="{{version}}"
|
||||
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "ERROR: Version '$VERSION' is not valid semver"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for uncommitted changes
|
||||
if ! git diff --quiet HEAD; then
|
||||
echo "ERROR: You have uncommitted changes. Please commit or stash them first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify version in source files matches tag version
|
||||
echo "Verifying version in source files..."
|
||||
|
||||
# Check __init__.py
|
||||
INIT_FILE="{{project_root}}/addon/FreecadRobustMCP/freecad_mcp_bridge/__init__.py"
|
||||
INIT_VERSION=$(grep -o '__version__ = "[^"]*"' "$INIT_FILE" | cut -d'"' -f2)
|
||||
if [ "$INIT_VERSION" != "$VERSION" ]; then
|
||||
echo "ERROR: Version mismatch in $INIT_FILE"
|
||||
echo " Expected: $VERSION"
|
||||
echo " Found: $INIT_VERSION"
|
||||
echo ""
|
||||
echo "Run 'just release::bump-workbench $VERSION' first, then commit the changes."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check package.xml
|
||||
PACKAGE_XML="{{project_root}}/package.xml"
|
||||
PKG_VERSION=$(awk '/<workbench>/,/<\/workbench>/' "$PACKAGE_XML" | grep -o '<version>[^<]*</version>' | head -1 | sed 's/<[^>]*>//g')
|
||||
if [ "$PKG_VERSION" != "$VERSION" ]; then
|
||||
echo "ERROR: Version mismatch in $PACKAGE_XML (workbench section)"
|
||||
echo " Expected: $VERSION"
|
||||
echo " Found: $PKG_VERSION"
|
||||
echo ""
|
||||
echo "Run 'just release::bump-workbench $VERSION' first, then commit the changes."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Version verification passed!"
|
||||
echo ""
|
||||
echo "Creating tag: $TAG"
|
||||
echo ""
|
||||
echo "This will trigger:"
|
||||
echo " - GitHub release with workbench archive"
|
||||
echo ""
|
||||
read -p "Continue? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git tag -a "$TAG" -m "Release MCP Bridge Workbench v{{version}}"
|
||||
git push origin "$TAG"
|
||||
echo ""
|
||||
echo "Tag $TAG created and pushed!"
|
||||
echo "Watch the release at: https://github.com/spkane/freecad-robust-mcp-and-more/actions"
|
||||
|
||||
# Private helper recipe for tagging macro releases
|
||||
# Parameters: macro_dir, macro_name, macro_file_basename, tag_prefix, bump_command, tag_message, version
|
||||
[private]
|
||||
_tag-macro macro_dir macro_name macro_file_basename tag_prefix bump_command tag_message version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="{{version}}"
|
||||
TAG="{{tag_prefix}}v{{version}}"
|
||||
MACRO_DIR="{{project_root}}/macros/{{macro_dir}}"
|
||||
MACRO_NAME="{{macro_name}}"
|
||||
|
||||
# Validate version format (SemVer 2.0)
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "ERROR: Version '$VERSION' is not valid semver"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for uncommitted changes
|
||||
if ! git diff --quiet HEAD; then
|
||||
echo "ERROR: You have uncommitted changes. Please commit or stash them first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify version in source files matches tag version
|
||||
echo "Verifying version in source files..."
|
||||
|
||||
# Check .FCMacro file
|
||||
MACRO_FILE="$MACRO_DIR/{{macro_file_basename}}"
|
||||
MACRO_VERSION=$(grep -o '__Version__ = "[^"]*"' "$MACRO_FILE" | cut -d'"' -f2)
|
||||
if [ "$MACRO_VERSION" != "$VERSION" ]; then
|
||||
echo "ERROR: Version mismatch in $MACRO_FILE"
|
||||
echo " Expected: $VERSION"
|
||||
echo " Found: $MACRO_VERSION"
|
||||
echo ""
|
||||
echo "Run 'just release::{{bump_command}} $VERSION' first, then commit the changes."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check package.xml
|
||||
PACKAGE_XML="{{project_root}}/package.xml"
|
||||
PKG_VERSION=$(awk -v name="$MACRO_NAME" '
|
||||
/<macro>/ { in_macro=1 }
|
||||
/<\/macro>/ { in_macro=0; found_name=0 }
|
||||
in_macro && index($0, name) > 0 { found_name=1 }
|
||||
in_macro && found_name && /<version>/ {
|
||||
gsub(/.*<version>/, ""); gsub(/<\/version>.*/, ""); print; exit
|
||||
}
|
||||
' "$PACKAGE_XML")
|
||||
if [ "$PKG_VERSION" != "$VERSION" ]; then
|
||||
echo "ERROR: Version mismatch in $PACKAGE_XML ($MACRO_NAME section)"
|
||||
echo " Expected: $VERSION"
|
||||
echo " Found: $PKG_VERSION"
|
||||
echo ""
|
||||
echo "Run 'just release::{{bump_command}} $VERSION' first, then commit the changes."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Version verification passed!"
|
||||
echo ""
|
||||
echo "Creating tag: $TAG"
|
||||
echo ""
|
||||
echo "This will trigger:"
|
||||
echo " - GitHub release with macro archive"
|
||||
echo ""
|
||||
read -p "Continue? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git tag -a "$TAG" -m "{{tag_message}} v{{version}}"
|
||||
git push origin "$TAG"
|
||||
echo ""
|
||||
echo "Tag $TAG created and pushed!"
|
||||
echo "Watch the release at: https://github.com/spkane/freecad-robust-mcp-and-more/actions"
|
||||
|
||||
# Create and push a release tag for the Cut Object for Magnets macro
|
||||
tag-macro-magnets version: (_tag-macro "Cut_Object_for_Magnets" "Cut Object for Magnets" "CutObjectForMagnets.FCMacro" "macro-cut-object-for-magnets-" "bump-macro-magnets" "Release Cut Object for Magnets Macro" version)
|
||||
|
||||
# Create and push a release tag for the Multi Export macro
|
||||
tag-macro-export version: (_tag-macro "Multi_Export" "Multi Export" "MultiExport.FCMacro" "macro-multi-export-" "bump-macro-export" "Release Multi Export Macro" version)
|
||||
|
||||
# =============================================================================
|
||||
# Tag Information Commands
|
||||
# =============================================================================
|
||||
|
||||
# List all release tags grouped by component
|
||||
list-tags:
|
||||
#!/usr/bin/env bash
|
||||
echo "=== MCP Server Releases ==="
|
||||
git tag -l 'robust-mcp-server-v*' --sort=-v:refname | head -10
|
||||
echo ""
|
||||
echo "=== MCP Workbench Releases ==="
|
||||
git tag -l 'robust-mcp-workbench-v*' --sort=-v:refname | head -10
|
||||
echo ""
|
||||
echo "=== Cut Object for Magnets Macro Releases ==="
|
||||
git tag -l 'macro-cut-object-for-magnets-v*' --sort=-v:refname | head -10
|
||||
echo ""
|
||||
echo "=== Multi Export Macro Releases ==="
|
||||
git tag -l 'macro-multi-export-v*' --sort=-v:refname | head -10
|
||||
|
||||
# Show the latest version of each component
|
||||
latest-versions:
|
||||
#!/usr/bin/env bash
|
||||
echo "Latest versions:"
|
||||
echo ""
|
||||
SERVER_TAG=$(git tag -l 'robust-mcp-server-v*' --sort=-v:refname | head -n1)
|
||||
WORKBENCH_TAG=$(git tag -l 'robust-mcp-workbench-v*' --sort=-v:refname | head -n1)
|
||||
MAGNETS_TAG=$(git tag -l 'macro-cut-object-for-magnets-v*' --sort=-v:refname | head -n1)
|
||||
EXPORT_TAG=$(git tag -l 'macro-multi-export-v*' --sort=-v:refname | head -n1)
|
||||
echo " MCP Server: ${SERVER_TAG:-none}"
|
||||
echo " MCP Workbench: ${WORKBENCH_TAG:-none}"
|
||||
echo " Macro Magnets: ${MAGNETS_TAG:-none}"
|
||||
echo " Macro Export: ${EXPORT_TAG:-none}"
|
||||
|
||||
# Show commits since the last release of a component
|
||||
changes-since component:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "{{component}}" in
|
||||
mcp-server|server)
|
||||
PREFIX="robust-mcp-server-v"
|
||||
PATHS="src/freecad_mcp pyproject.toml Dockerfile"
|
||||
;;
|
||||
workbench)
|
||||
PREFIX="robust-mcp-workbench-v"
|
||||
PATHS="addon/FreecadRobustMCP"
|
||||
;;
|
||||
macro-magnets|magnets)
|
||||
PREFIX="macro-cut-object-for-magnets-v"
|
||||
PATHS="macros/Cut_Object_for_Magnets"
|
||||
;;
|
||||
macro-export|export)
|
||||
PREFIX="macro-multi-export-v"
|
||||
PATHS="macros/Multi_Export"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown component: {{component}}"
|
||||
echo "Valid: mcp-server, workbench, macro-magnets, macro-export"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
LATEST_TAG=$(git tag -l "${PREFIX}*" --sort=-v:refname | head -1)
|
||||
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
echo "No previous releases found for {{component}}"
|
||||
echo "Showing all commits for relevant paths:"
|
||||
git log --oneline -- $PATHS | head -20
|
||||
else
|
||||
echo "Changes since $LATEST_TAG:"
|
||||
echo ""
|
||||
git log --oneline "$LATEST_TAG"..HEAD -- $PATHS
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Tag Management
|
||||
# =============================================================================
|
||||
|
||||
# Delete a release tag (local and remote)
|
||||
delete-tag tag:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "This will delete the tag '{{tag}}' both locally and from the remote."
|
||||
echo ""
|
||||
read -p "Are you sure? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Delete local tag
|
||||
if git tag -l '{{tag}}' | grep -F -q '{{tag}}'; then
|
||||
git tag -d '{{tag}}'
|
||||
echo "Deleted local tag: {{tag}}"
|
||||
else
|
||||
echo "Local tag '{{tag}}' not found (may already be deleted)"
|
||||
fi
|
||||
|
||||
# Delete remote tag
|
||||
if git ls-remote --tags origin | grep -F -q 'refs/tags/{{tag}}'; then
|
||||
git push origin --delete '{{tag}}'
|
||||
echo "Deleted remote tag: {{tag}}"
|
||||
else
|
||||
echo "Remote tag '{{tag}}' not found (may already be deleted)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Tag '{{tag}}' deleted."
|
||||
|
||||
# =============================================================================
|
||||
# Release Status
|
||||
# =============================================================================
|
||||
|
||||
# Show unreleased changes across all components
|
||||
status:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "=========================================="
|
||||
echo "Release Status - Unreleased Changes"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Helper function to count changes
|
||||
count_changes() {
|
||||
local prefix="$1"
|
||||
local paths="$2"
|
||||
local latest_tag=$(git tag -l "${prefix}*" --sort=-v:refname | head -1)
|
||||
|
||||
if [ -z "$latest_tag" ]; then
|
||||
# No releases yet, count all commits
|
||||
git log --oneline -- $paths 2>/dev/null | wc -l | tr -d ' '
|
||||
else
|
||||
git log --oneline "$latest_tag"..HEAD -- $paths 2>/dev/null | wc -l | tr -d ' '
|
||||
fi
|
||||
}
|
||||
|
||||
# MCP Server
|
||||
SERVER_CHANGES=$(count_changes "robust-mcp-server-v" "src/freecad_mcp pyproject.toml Dockerfile")
|
||||
SERVER_TAG=$(git tag -l 'robust-mcp-server-v*' --sort=-v:refname | head -1)
|
||||
if [ "$SERVER_CHANGES" -gt 0 ]; then
|
||||
echo "MCP Server: $SERVER_CHANGES unreleased commit(s)"
|
||||
echo " Latest: ${SERVER_TAG:-none}"
|
||||
echo " View: just release::changes-since mcp-server"
|
||||
else
|
||||
echo "MCP Server: up to date (${SERVER_TAG:-no releases})"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Workbench
|
||||
WORKBENCH_CHANGES=$(count_changes "robust-mcp-workbench-v" "addon/FreecadRobustMCP")
|
||||
WORKBENCH_TAG=$(git tag -l 'robust-mcp-workbench-v*' --sort=-v:refname | head -1)
|
||||
if [ "$WORKBENCH_CHANGES" -gt 0 ]; then
|
||||
echo "MCP Workbench: $WORKBENCH_CHANGES unreleased commit(s)"
|
||||
echo " Latest: ${WORKBENCH_TAG:-none}"
|
||||
echo " View: just release::changes-since workbench"
|
||||
else
|
||||
echo "MCP Workbench: up to date (${WORKBENCH_TAG:-no releases})"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Macro Magnets
|
||||
MAGNETS_CHANGES=$(count_changes "macro-cut-object-for-magnets-v" "macros/Cut_Object_for_Magnets")
|
||||
MAGNETS_TAG=$(git tag -l 'macro-cut-object-for-magnets-v*' --sort=-v:refname | head -1)
|
||||
if [ "$MAGNETS_CHANGES" -gt 0 ]; then
|
||||
echo "Macro (Cut Object for Magnets): $MAGNETS_CHANGES unreleased commit(s)"
|
||||
echo " Latest: ${MAGNETS_TAG:-none}"
|
||||
echo " View: just release::changes-since macro-magnets"
|
||||
else
|
||||
echo "Macro (Cut Object for Magnets): up to date (${MAGNETS_TAG:-no releases})"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Macro Export
|
||||
EXPORT_CHANGES=$(count_changes "macro-multi-export-v" "macros/Multi_Export")
|
||||
EXPORT_TAG=$(git tag -l 'macro-multi-export-v*' --sort=-v:refname | head -1)
|
||||
if [ "$EXPORT_CHANGES" -gt 0 ]; then
|
||||
echo "Macro (Multi Export): $EXPORT_CHANGES unreleased commit(s)"
|
||||
echo " Latest: ${EXPORT_TAG:-none}"
|
||||
echo " View: just release::changes-since macro-export"
|
||||
else
|
||||
echo "Macro (Multi Export): up to date (${EXPORT_TAG:-no releases})"
|
||||
fi
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
|
||||
# =============================================================================
|
||||
# Changelog / Release Notes Helpers
|
||||
# =============================================================================
|
||||
|
||||
# Draft release notes for a component by extracting conventional commits since last release
|
||||
draft-notes component:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "{{component}}" in
|
||||
mcp-server|server)
|
||||
PREFIX="robust-mcp-server-v"
|
||||
PATHS="src/freecad_mcp pyproject.toml Dockerfile"
|
||||
COMPONENT_NAME="MCP Server"
|
||||
;;
|
||||
workbench)
|
||||
PREFIX="robust-mcp-workbench-v"
|
||||
PATHS="addon/FreecadRobustMCP"
|
||||
COMPONENT_NAME="MCP Bridge Workbench"
|
||||
;;
|
||||
macro-magnets|magnets)
|
||||
PREFIX="macro-cut-object-for-magnets-v"
|
||||
PATHS="macros/Cut_Object_for_Magnets"
|
||||
COMPONENT_NAME="Cut Object for Magnets Macro"
|
||||
;;
|
||||
macro-export|export)
|
||||
PREFIX="macro-multi-export-v"
|
||||
PATHS="macros/Multi_Export"
|
||||
COMPONENT_NAME="Multi Export Macro"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown component: {{component}}"
|
||||
echo "Valid: mcp-server, workbench, macro-magnets, macro-export"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
LATEST_TAG=$(git tag -l "${PREFIX}*" --sort=-v:refname | head -1)
|
||||
|
||||
echo "# Draft Release Notes for $COMPONENT_NAME"
|
||||
echo ""
|
||||
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
echo "No previous releases found. Showing all commits for component paths."
|
||||
echo ""
|
||||
REV_RANGE="HEAD"
|
||||
else
|
||||
echo "Changes since: $LATEST_TAG"
|
||||
echo ""
|
||||
REV_RANGE="${LATEST_TAG}..HEAD"
|
||||
fi
|
||||
|
||||
# Get commits and categorize by conventional commit type
|
||||
echo "## Added"
|
||||
echo ""
|
||||
git log --oneline "$REV_RANGE" -- $PATHS 2>/dev/null | grep -iE "^[a-f0-9]+ feat(\(|:)" | sed -E 's/^[a-f0-9]+ /- /' || true
|
||||
echo ""
|
||||
|
||||
echo "## Changed"
|
||||
echo ""
|
||||
git log --oneline "$REV_RANGE" -- $PATHS 2>/dev/null | grep -iE "^[a-f0-9]+ (refactor|perf|style)(\(|:)" | sed -E 's/^[a-f0-9]+ /- /' || true
|
||||
echo ""
|
||||
|
||||
echo "## Fixed"
|
||||
echo ""
|
||||
git log --oneline "$REV_RANGE" -- $PATHS 2>/dev/null | grep -iE "^[a-f0-9]+ fix(\(|:)" | sed -E 's/^[a-f0-9]+ /- /' || true
|
||||
echo ""
|
||||
|
||||
echo "## Documentation"
|
||||
echo ""
|
||||
git log --oneline "$REV_RANGE" -- $PATHS 2>/dev/null | grep -iE "^[a-f0-9]+ docs(\(|:)" | sed -E 's/^[a-f0-9]+ /- /' || true
|
||||
echo ""
|
||||
|
||||
echo "## Other Changes"
|
||||
echo ""
|
||||
git log --oneline "$REV_RANGE" -- $PATHS 2>/dev/null | grep -ivE "^[a-f0-9]+ (feat|fix|refactor|perf|style|docs|test|ci|build|chore)(\(|:)" | sed -E 's/^[a-f0-9]+ /- /' || true
|
||||
echo ""
|
||||
|
||||
echo "---"
|
||||
echo ""
|
||||
echo "## All Commits (chronological)"
|
||||
echo ""
|
||||
git log --oneline "$REV_RANGE" -- $PATHS 2>/dev/null | sed -E 's/^[a-f0-9]+ /- /' || echo "(no commits)"
|
||||
|
||||
# Extract changelog section for a specific component version (for GitHub Release body)
|
||||
extract-changelog component version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Match header exactly as it appears in CHANGELOG.md
|
||||
case "{{component}}" in
|
||||
mcp-server|server)
|
||||
HEADER="### MCP Server v{{version}}"
|
||||
;;
|
||||
workbench)
|
||||
HEADER="### MCP Bridge Workbench v{{version}}"
|
||||
;;
|
||||
macro-magnets|magnets)
|
||||
HEADER="### Cut Object for Magnets Macro v{{version}}"
|
||||
;;
|
||||
macro-export|export)
|
||||
HEADER="### Multi Export Macro v{{version}}"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown component: {{component}}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
CHANGELOG="{{project_root}}/CHANGELOG.md"
|
||||
|
||||
# Extract section between this version header and the next component header or separator
|
||||
# Only exit on: "---" separator OR "### " followed by component name (capital letter)
|
||||
# This allows #### Added, #### Changed, etc. to be included
|
||||
awk -v header="$HEADER" '
|
||||
BEGIN { found=0 }
|
||||
$0 == header || $0 == header " " { found=1; next }
|
||||
found && /^---$/ { exit }
|
||||
found && /^### [A-Z]/ { exit }
|
||||
found { print }
|
||||
' "$CHANGELOG"
|
||||
|
||||
# =============================================================================
|
||||
# Dry Run Commands (preview without pushing)
|
||||
# =============================================================================
|
||||
|
||||
# Preview what a release tag would look like (no actual tag created)
|
||||
dry-run-tag component version:
|
||||
#!/usr/bin/env bash
|
||||
# Validate version format (SemVer 2.0)
|
||||
if [[ ! "{{version}}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "Error: Invalid version format '{{version}}'"
|
||||
echo "Expected: X.Y.Z or X.Y.Z-prerelease (e.g., 1.0.0, 1.0.0-alpha, 1.0.0-beta.1)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "{{component}}" in
|
||||
mcp-server|server)
|
||||
TAG="robust-mcp-server-v{{version}}"
|
||||
echo "Would create tag: $TAG"
|
||||
echo "Triggers: PyPI, Docker Hub, GitHub Release"
|
||||
;;
|
||||
workbench)
|
||||
TAG="robust-mcp-workbench-v{{version}}"
|
||||
echo "Would create tag: $TAG"
|
||||
echo "Triggers: GitHub Release with workbench archive"
|
||||
;;
|
||||
macro-magnets|magnets)
|
||||
TAG="macro-cut-object-for-magnets-v{{version}}"
|
||||
echo "Would create tag: $TAG"
|
||||
echo "Triggers: GitHub Release with macro archive"
|
||||
;;
|
||||
macro-export|export)
|
||||
TAG="macro-multi-export-v{{version}}"
|
||||
echo "Would create tag: $TAG"
|
||||
echo "Triggers: GitHub Release with macro archive"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown component: {{component}}"
|
||||
echo "Valid: mcp-server, workbench, macro-magnets, macro-export"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
+15
-8
@@ -1,32 +1,39 @@
|
||||
# Testing commands
|
||||
# Usage: just testing::unit, just testing::integration, etc.
|
||||
|
||||
# Project root directory (justfile_directory() returns the main justfile's directory)
|
||||
project_root := justfile_directory()
|
||||
|
||||
# Run unit tests only (excludes integration tests)
|
||||
unit:
|
||||
uv run pytest --ignore=tests/integration
|
||||
uv run pytest {{project_root}}/tests/unit
|
||||
|
||||
# Run tests with coverage (excludes integration tests)
|
||||
cov:
|
||||
uv run pytest --ignore=tests/integration --cov=src/freecad_mcp --cov-report=term-missing --cov-report=html
|
||||
cd {{project_root}} && uv run pytest tests/unit --cov=freecad_mcp --cov-report=term-missing --cov-report=html:{{project_root}}/htmlcov
|
||||
|
||||
# Run tests without slow markers (excludes integration tests)
|
||||
fast:
|
||||
uv run pytest --ignore=tests/integration -m "not slow"
|
||||
uv run pytest {{project_root}}/tests/unit -m "not slow"
|
||||
|
||||
# Run only integration tests (requires running FreeCAD MCP bridge)
|
||||
integration:
|
||||
uv run pytest tests/integration -v
|
||||
uv run pytest {{project_root}}/tests/integration -v
|
||||
|
||||
# Run tests with verbose output (excludes integration tests)
|
||||
verbose:
|
||||
uv run pytest --ignore=tests/integration -v --tb=long
|
||||
uv run pytest {{project_root}}/tests/unit -v --tb=long
|
||||
|
||||
# Run all tests including integration (requires running FreeCAD MCP bridge)
|
||||
all:
|
||||
uv run pytest
|
||||
uv run pytest {{project_root}}/tests
|
||||
|
||||
# Run tests in watch mode (re-runs on file changes)
|
||||
watch:
|
||||
uv run pytest-watch {{project_root}}/tests/unit
|
||||
|
||||
# Run integration tests with automatic FreeCAD headless startup
|
||||
integration-auto:
|
||||
integration-freecad:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
echo "Starting FreeCAD headless server for integration tests..."
|
||||
@@ -59,7 +66,7 @@ integration-auto:
|
||||
|
||||
# Run integration tests
|
||||
TEST_EXIT_CODE=0
|
||||
uv run pytest tests/integration -v || TEST_EXIT_CODE=$?
|
||||
uv run pytest "{{project_root}}/tests/integration" -v || TEST_EXIT_CODE=$?
|
||||
|
||||
# Stop FreeCAD
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user