fix: improve release note process (#36)

* fix: improve release note process

* docs: improve release notes

* fix: correct repo name/dir

* fix: CVE resolution for jaraco.context

* fix: docker build with uv.lovl

* fix: CVE remediations

* fix: update docker workflow

* fix: remove setuptools from base image
This commit is contained in:
Sean P. Kane
2026-01-16 13:51:08 -08:00
committed by GitHub
parent b896a69b84
commit 62f6f4dbcf
21 changed files with 456 additions and 271 deletions
+1 -1
View File
@@ -95,5 +95,5 @@ justfile
.codespell-ignore-words.txt
mkdocs.yml
ARCHITECTURE-MCP.md
CHANGELOG.md
CLAUDE.md
**/RELEASE_NOTES.md
+7 -2
View File
@@ -11,6 +11,7 @@ on:
- ".dockerignore"
- "src/**/*.py"
- "pyproject.toml"
- "uv.lock"
- ".github/workflows/docker.yaml"
pull_request:
branches: [main, master]
@@ -19,6 +20,7 @@ on:
- ".dockerignore"
- "src/**/*.py"
- "pyproject.toml"
- "uv.lock"
- ".github/workflows/docker.yaml"
workflow_dispatch:
workflow_call:
@@ -106,8 +108,8 @@ jobs:
- name: Test Docker image
run: |
# Build for current platform only for testing
docker build --build-arg VERSION=${{ steps.version.outputs.VERSION }} -t ${{ env.IMAGE_NAME }}:test .
# Build for current platform only for testing (--no-cache ensures fresh build)
docker build --no-cache --build-arg VERSION=${{ steps.version.outputs.VERSION }} -t ${{ env.IMAGE_NAME }}:test .
# Test that the container starts and responds to MCP initialize
echo '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | \
@@ -129,6 +131,7 @@ jobs:
uses: aquasecurity/trivy-action@0.33.1
with:
image-ref: ${{ env.IMAGE_NAME }}:test
version: "v0.68.2"
severity: "HIGH,CRITICAL"
exit-code: "1"
format: "table"
@@ -139,6 +142,7 @@ jobs:
uses: aquasecurity/trivy-action@0.33.1
with:
image-ref: ${{ env.IMAGE_NAME }}:test
version: "v0.68.2"
severity: "MEDIUM,LOW"
exit-code: "0"
format: "table"
@@ -151,6 +155,7 @@ jobs:
uses: aquasecurity/trivy-action@0.33.1
with:
image-ref: ${{ env.IMAGE_NAME }}:test
version: "v0.68.2"
format: "sarif"
output: "trivy-results.sarif"
cache-dir: .trivy-cache
+10 -11
View File
@@ -222,7 +222,7 @@ jobs:
echo "Created archives:"
ls -la "${ARCHIVE_NAME}-${VERSION}."*
- name: Extract changelog section
- name: Extract release notes section
id: changelog
env:
VERSION: ${{ steps.version.outputs.version }}
@@ -233,19 +233,18 @@ jobs:
README_FILE: ${{ inputs.readme_file }}
WIKI_URL: ${{ inputs.wiki_url }}
run: |
# Match header exactly as it appears in CHANGELOG.md
HEADER="### ${MACRO_NAME} Macro v${VERSION}"
RELEASE_NOTES="macros/${MACRO_DIR}/RELEASE_NOTES.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
CHANGELOG_CONTENT=$(awk -v header="$HEADER" '
# Extract section for this version from RELEASE_NOTES.md
# Format: ## Version X.Y.Z (date)
CHANGELOG_CONTENT=$(awk -v version="$VERSION" '
BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next }
found && /^---$/ { exit }
found && /^### [A-Z]/ { exit }
/^## Version / {
if (found) exit
if (index($0, version) > 0) { found=1; next }
}
found { print }
' CHANGELOG.md)
' "$RELEASE_NOTES" 2>/dev/null || echo "")
# Build release body
cat > release_body.md << EOF
+13 -13
View File
@@ -347,23 +347,23 @@ jobs:
name: python-package-distributions
path: dist/
- name: Extract changelog section
- name: Extract release notes section
id: changelog
run: |
VERSION="${{ needs.validate-tag.outputs.version }}"
# Match header exactly as it appears in CHANGELOG.md
HEADER="### Robust MCP Server v${VERSION}"
RELEASE_NOTES="src/freecad_mcp/RELEASE_NOTES.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
CHANGELOG_CONTENT=$(awk -v header="$HEADER" '
# Extract section for this version from RELEASE_NOTES.md
# Format: ## Version X.Y.Z (date)
# Extract everything between this version header and the next ## Version header
CHANGELOG_CONTENT=$(awk -v version="$VERSION" '
BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next }
found && /^---$/ { exit }
found && /^### [A-Z]/ { exit }
/^## Version / {
if (found) exit
if (index($0, version) > 0) { found=1; next }
}
found { print }
' CHANGELOG.md)
' "$RELEASE_NOTES")
# Write to file for the release body (handles multiline)
echo "$CHANGELOG_CONTENT" > changelog_section.md
@@ -371,10 +371,10 @@ jobs:
# Check if we got content
if [ -n "$CHANGELOG_CONTENT" ]; then
echo "found=true" >> "$GITHUB_OUTPUT"
echo "Extracted changelog section for $HEADER"
echo "Extracted release notes for version $VERSION"
else
echo "found=false" >> "$GITHUB_OUTPUT"
echo "No changelog section found for $HEADER"
echo "No release notes found for version $VERSION (will use auto-generated notes)"
fi
- name: Create GitHub Release
+13 -11
View File
@@ -162,23 +162,25 @@ jobs:
echo "Created archives:"
ls -la "freecad-mcp-workbench-${VERSION}."*
- name: Extract changelog section
- name: Extract release notes section
id: changelog
run: |
VERSION="${{ steps.version.outputs.version }}"
# Match header exactly as it appears in CHANGELOG.md (with optional space before v)
HEADER="### Robust MCP Bridge Workbench v${VERSION}"
RELEASE_NOTES="addon/FreecadRobustMCPBridge/RELEASE_NOTES.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
CHANGELOG_CONTENT=$(awk -v header="$HEADER" '
# Extract section for this version from RELEASE_NOTES.md
# Format: ## Version X.Y.Z (date)
# Use exact version match to avoid substring issues (e.g., 1.0.0 matching 1.0.0-alpha)
CHANGELOG_CONTENT=$(awk -v version="$VERSION" '
BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next }
found && /^---$/ { exit }
found && /^### [A-Z]/ { exit }
/^## Version / {
if (found) exit
# Extract version field (3rd word) and compare exactly
split($0, parts, " ")
if (parts[3] == version) { found=1; next }
}
found { print }
' CHANGELOG.md)
' "$RELEASE_NOTES" 2>/dev/null || echo "")
# Build release body with changelog content if available
cat > release_body.md << 'STATIC_EOF'
+1 -1
View File
@@ -13,7 +13,7 @@ pre-commit = "4.5" # pre-commit hooks
github-cli = "2.74" # GitHub CLI for PR/issue management
# Security and code quality tools
trivy = "0.62" # container vulnerability scanner
trivy = "0.68" # container vulnerability scanner
gitleaks = "8.30" # secrets scanner
actionlint = "1.7" # GitHub Actions linter
# Note: hadolint and shellcheck are managed by their pre-commit repos
+3 -3
View File
@@ -1,5 +1,5 @@
[project]
id = freecad-mcp-and-more
url = /codebases/freecad-mcp-and-more/findings
name = freecad-mcp-and-more
id = freecad-addon-robust-mcp-server
url = /codebases/freecad-addon-robust-mcp-server/findings
name = freecad-addon-robust-mcp-server
+1 -1
View File
@@ -92,7 +92,7 @@
},
{
"path": "detect_secrets.filters.common.is_baseline_file",
"filename": "/Users/spkane/dev/spkane/freecad-mcp-and-more/.secrets.baseline"
"filename": "/Users/spkane/dev/spkane/FreeCAD-Components/mine/freecad-addon-robust-mcp-server/.secrets.baseline"
},
{
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
-124
View File
@@ -1,124 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
This is a multi-component project. Each component has its own versioning and release cycle:
| Component | Tag Format | Distribution |
| ------------------------------ | ------------------------------------- | ------------------------ |
| Robust MCP Server | `robust-mcp-server-vX.Y.Z` | PyPI, Docker Hub, GitHub |
| Robust MCP Bridge Workbench | `robust-mcp-workbench-vX.Y.Z` | GitHub Release |
| Cut Object for Magnets Macro | `macro-cut-object-for-magnets-vX.Y.Z` | GitHub Release |
| Multi Export Macro | `macro-multi-export-vX.Y.Z` | GitHub Release |
---
## [Unreleased]
### Robust MCP Server
#### Removed
- Docker detection fields (`in_docker`, `docker_container_id`) from `get_mcp_server_environment()` tool
### Robust MCP Bridge Workbench
#### Fixed
- Fixed GUI crashes during auto-start by deferring bridge initialization 2 seconds after `FreeCAD.GuiUp` becomes True
- Increased GUI wait timeout from 3 seconds to 60 seconds to accommodate slow FreeCAD startups on macOS
- Bridge no longer attempts to start on timeout (prevents crash from background thread usage)
- `startup_bridge.py` now has same defensive GUI wait logic as `Init.py`
### Cut Object for Magnets Macro
### Multi Export Macro
---
## Initial Public Beta - 2026-01-05
### Robust MCP Server v0.5.0-beta
Initial public beta release.
#### Added
- **Document management**: Create, open, save, close, recompute documents
- **Object manipulation**: Create primitives (box, cylinder, sphere, cone, torus, wedge, helix), boolean operations, transforms (scale, rotate, copy, mirror)
- **PartDesign workflow**: Bodies, sketches, pads, pockets, revolutions, fillets, chamfers, holes, patterns, lofts, sweeps
- **Import/Export**: STEP, STL, 3MF, OBJ, IGES, BREP formats
- **View control**: Screenshots, camera positioning, zoom, visibility, display modes
- **Macro support**: List, run, create, and manage FreeCAD macros
- **Undo/Redo**: Full undo/redo support with status queries
- **Connection modes**: XML-RPC (recommended), Socket, and Embedded (Linux only)
#### Installation
- **PyPI**: `pip install freecad-robust-mcp`
- **Docker**: `docker pull ghcr.io/spkane/freecad-robust-mcp`
- **Source**: Clone and install with `uv sync --all-extras`
#### Known Limitations
- Embedded mode only works on Linux (macOS/Windows must use xmlrpc or socket)
- Python 3.11 required (must match FreeCAD's bundled Python for ABI compatibility)
- GUI features (screenshots, visibility) not available in headless mode
---
### Robust MCP Bridge Workbench v0.5.0-beta
Initial public beta release.
#### Added
- **FreeCAD Workbench**: Installable via FreeCAD Addon Manager
- **XML-RPC server**: Runs on port 9875 for Robust MCP Server communication
- **Socket server**: Alternative JSON-RPC on port 9876
- **Auto-start option**: Configure bridge to start with FreeCAD
- **GUI mode support**: Full 3D view integration with screenshots
- **Headless mode support**: Console-only operation for automation
---
### Cut Object for Magnets Macro v0.5.0-beta
Initial public beta release.
#### Added
- **Smart cutting**: Cut objects in half with aligned magnet holes for easy reassembly
- **Hole placement**: Automatic hole placement with overlap detection
- **Object support**: Works with solid and hollow objects
- **Hole methods**: Parametric PartDesign::Hole or boolean hole methods
- **GUI dialog**: Interactive parameter configuration
---
### Multi Export Macro v0.5.0-beta
Initial public beta release.
#### Added
- **Multi-format export**: Export selected objects to multiple formats simultaneously
- **Format support**: STL, STEP, 3MF, OBJ, IGES, BREP, PLY, AMF
- **Mesh control**: Configurable mesh tolerance for STL/3MF
- **Batch export**: Export multiple objects with progress feedback
- **GUI dialog**: Interactive format and option selection
---
## Project Infrastructure (v0.5.0-beta)
### Added
- **CI/CD**: GitHub Actions workflows for testing, Docker builds, and PyPI releases
- **Quality**: Pre-commit hooks with Ruff, MyPy, Bandit, and secrets detection
- **Dependencies**: Dependabot for automated dependency updates
- **Security**: CodeQL security scanning
- **Documentation**: Comprehensive README, CLAUDE.md, and architecture docs
+82 -26
View File
@@ -800,7 +800,6 @@ project-root/
├── .pre-commit-config.yaml # Pre-commit hook configuration
├── .safety-policy.yml # Safety CLI scan policy
├── .secrets.baseline # detect-secrets baseline
├── CHANGELOG.md # Project changelog
├── CLAUDE.md # This file (AI assistant guidelines)
├── Dockerfile # Docker image definition
├── justfile # Main task runner (imports modules)
@@ -1375,11 +1374,20 @@ This project uses **component-specific versioning**. Each component has its own
*Stable releases (`X.Y.Z`) publish to PyPI; non-stable releases (alpha, beta, rc) publish to TestPyPI only.
### Changelog Management
### Release Notes Management
The project uses a single `CHANGELOG.md` with sections for each component. Before releasing:
Each component has its own `RELEASE_NOTES.md` file. Release workflows automatically extract the relevant section for GitHub Releases.
1. **Draft release notes** from conventional commits:
| Component | Release Notes File |
| ---------------------------- | ----------------------------------------------------- |
| MCP Server | `src/freecad_mcp/RELEASE_NOTES.md` |
| Robust MCP Bridge Workbench | `addon/FreecadRobustMCPBridge/RELEASE_NOTES.md` |
| Cut Object for Magnets Macro | `macros/Cut_Object_for_Magnets/RELEASE_NOTES.md` |
| Multi Export Macro | `macros/Multi_Export/RELEASE_NOTES.md` |
**Before releasing a component:**
1. **Draft release notes** from git commits since the last release:
```bash
just release::draft-notes mcp-server
@@ -1388,36 +1396,61 @@ The project uses a single `CHANGELOG.md` with sections for each component. Befor
just release::draft-notes macro-export
```
2. **Update CHANGELOG.md** with entries under the appropriate component header:
2. **Edit the component's RELEASE_NOTES.md** file, adding a new version section at the top:
```markdown
## YYYY-MM-DD
## Version X.Y.Z (YYYY-MM-DD)
### MCP Server vX.Y.Z
Release notes for changes between vA.B.C and vX.Y.Z.
#### Added
- New feature
### Added
---
- New feature description
### Robust MCP Bridge Workbench vX.Y.Z
...
### Changed
- Change description
### Fixed
- Bug fix description
```
3. The release workflow automatically extracts the changelog section for GitHub Releases.
3. The release workflow automatically extracts the version section for GitHub Releases.
### Creating a Release
Use the `just release::` commands to create and push release tags:
**Step 1: Run release tests** to ensure everything passes:
```bash
# Check what has unreleased changes
just release::status
just testing::release-test
```
# Preview changes since last release
just release::changes-since mcp-server
just release::changes-since workbench
This runs unit tests, integration tests (headless + GUI), Docker tests, and just command tests. All must pass before proceeding.
**Step 2: Update release notes** for each component you're releasing:
```bash
# Generate draft notes from git commits
just release::draft-notes mcp-server
# Edit the RELEASE_NOTES.md file (add version section at top)
# See "Release Notes Management" above for format
```
**Step 3: Bump versions** (for workbench/macros only - MCP server auto-bumps from tag):
```bash
just release::bump-workbench 1.0.0
just release::bump-macro-magnets 1.0.0
just release::bump-macro-export 1.0.0
```
**Step 4: Commit and push** your RELEASE_NOTES.md and version bump changes.
**Step 5: Create release tags** (this triggers the release workflows):
```bash
# Release the MCP server (triggers PyPI, Docker, GitHub release)
just release::tag-mcp-server 1.0.0
@@ -1427,10 +1460,23 @@ just release::tag-workbench 1.0.0
# Release macros
just release::tag-macro-magnets 1.0.0
just release::tag-macro-export 1.0.0
```
**Utility commands:**
```bash
# Check what has unreleased changes
just release::status
# Preview changes since last release
just release::changes-since mcp-server
# View release tags
just release::list-tags
just release::latest-versions
# Extract changelog for a version (for testing)
just release::extract-changelog mcp-server 1.0.0
```
### Version Format
@@ -1485,14 +1531,24 @@ Each component can have a different version, and the release workflows automatic
---
## Changelog Maintenance
## Release Notes Maintenance
**IMPORTANT**: Update `CHANGELOG.md` at the end of any session where files in the repository are modified.
Each component has its own `RELEASE_NOTES.md` file that is updated before releases. Release workflows automatically extract the relevant version section for GitHub Releases.
- Follow [Keep a Changelog](https://keepachangelog.com/) format
- Group changes under: Added, Changed, Deprecated, Removed, Fixed, Security
- Include date for each release version
- Document all significant changes, not just code changes
**When preparing a release:**
1. Use `just release::draft-notes <component>` to generate draft notes from commits
2. Edit the component's `RELEASE_NOTES.md` file, adding a new version section at the top
3. Follow the format: `## Version X.Y.Z (YYYY-MM-DD)` with `### Added`, `### Changed`, `### Fixed` sections
**Release notes files:**
| Component | File |
| ---------------------------- | ------------------------------------------------ |
| MCP Server | `src/freecad_mcp/RELEASE_NOTES.md` |
| Robust MCP Bridge Workbench | `addon/FreecadRobustMCPBridge/RELEASE_NOTES.md` |
| Cut Object for Magnets Macro | `macros/Cut_Object_for_Magnets/RELEASE_NOTES.md` |
| Multi Export Macro | `macros/Multi_Export/RELEASE_NOTES.md` |
---
@@ -1707,4 +1763,4 @@ When working on this project, ALWAYS:
- [ ] Run `just all` before finishing - everything must pass
- [ ] Use latest stable library versions
- [ ] Follow security best practices
- [ ] Update `CHANGELOG.md` with session changes
- [ ] Update component `RELEASE_NOTES.md` files before releases
+15 -9
View File
@@ -36,7 +36,8 @@ RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-cache-dir --no-compile uv
# Copy only dependency files first for better layer caching
COPY pyproject.toml README.md ./
# Include uv.lock for reproducible builds with locked dependency versions
COPY pyproject.toml uv.lock README.md ./
COPY src/ ./src/
# Version for setuptools-scm when building without git (e.g., in Docker)
@@ -44,12 +45,12 @@ COPY src/ ./src/
ARG VERSION=0.0.0.dev0
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${VERSION}
# Create virtual environment and install dependencies
# Create virtual environment and install dependencies using locked versions
# Using uv cache mount for faster rebuilds
# --frozen ensures uv.lock is used exactly without updates
RUN --mount=type=cache,target=/root/.cache/uv \
uv venv /opt/venv && \
. /opt/venv/bin/activate && \
uv pip install --no-compile .
UV_PROJECT_ENVIRONMENT=/opt/venv uv sync --frozen --no-dev --no-editable
# =============================================================================
# Stage 2: Runtime - Minimal image for running the server
@@ -77,12 +78,17 @@ RUN apk upgrade --no-cache
RUN addgroup -g 1000 mcpuser && \
adduser -u 1000 -G mcpuser -s /bin/sh -D mcpuser
# Upgrade system pip to fix CVE-2025-8869 (defense-in-depth)
# Note: Although PATH prefers /opt/venv/bin, we upgrade the system pip at
# /usr/local/bin/pip intentionally. This ensures no vulnerable pip exists
# in the image, even if the venv is bypassed or pip is invoked directly.
# Remove pip, setuptools, and wheel from system Python to fix CVEs
# - The base image has pip with CVE-2025-8869
# - setuptools vendors jaraco.context 5.3.0 with GHSA-58pv-8j8x-9vj2
# Since we use a pre-built venv, we don't need these in system Python at runtime.
# This eliminates the vulnerabilities without affecting functionality.
# hadolint ignore=DL3013
RUN pip install --no-cache-dir --upgrade "pip>=25.3"
RUN pip uninstall -y pip setuptools wheel 2>/dev/null || true && \
rm -rf /usr/local/lib/python3.11/site-packages/pip* \
/usr/local/lib/python3.11/site-packages/setuptools* \
/usr/local/lib/python3.11/site-packages/wheel* \
/usr/local/lib/python3.11/site-packages/pkg_resources*
# Copy virtual environment from builder
COPY --from=builder /opt/venv /opt/venv
@@ -0,0 +1,69 @@
# Robust MCP Bridge Workbench Release Notes
## Version 0.6.1 (2026-01-12)
Release notes for changes between v0.5.0-beta and v0.6.1.
### Major Change: Macro to Workbench
The MCP Bridge has been completely rewritten from a simple macro (`Start MCP Bridge`) to a full **FreeCAD Workbench**. This provides:
- Native FreeCAD Addon Manager installation
- Integrated toolbar with start/stop/status controls
- Preferences panel for configuration
- Real-time status widget showing connection state
- Proper lifecycle management with FreeCAD
### Added
- **FreeCAD Workbench**: Full workbench with toolbar, icons, and menus
- **Addon Manager support**: Install directly from FreeCAD's Addon Manager
- **Preferences panel**: Configure ports, auto-start, and logging from Edit > Preferences
- **Status widget**: Real-time display of bridge status, connected clients, and uptime
- **Auto-start option**: Optionally start the bridge automatically when FreeCAD launches
- **Start/Stop commands**: Toolbar buttons and menu items to control the bridge
- **Custom icons**: Professional SVG icons for all commands and status indicators
- **GUI mode support**: Full integration with FreeCAD's 3D view for screenshots
- **Headless mode support**: Works with `freecadcmd` for automation pipelines
### Changed
- **Architecture**: Complete rewrite from single macro to modular workbench structure
- **Server code**: Bridge server now lives in `freecad_mcp_bridge/` package within the addon
- **Configuration**: Settings now stored in FreeCAD's preference system instead of environment variables
- **Startup behavior**: Bridge waits for GUI initialization before starting (prevents crashes)
### Fixed
- **GUI crash on macOS**: Fixed race condition where bridge started before Qt event loop was ready
- **Thread safety**: All FreeCAD operations now execute on the main thread via queue processor
- **Startup timeout**: Increased GUI wait timeout to 60 seconds for slow FreeCAD startups
- **Clean shutdown**: Proper cleanup of servers and threads when FreeCAD exits
### Removed
- **Start MCP Bridge macro**: Replaced by the workbench (macro no longer needed)
### Installation
**Via FreeCAD Addon Manager (Recommended):**
1. Open FreeCAD
2. Go to Tools > Addon Manager
3. Search for "Robust MCP Bridge"
4. Click Install
5. Restart FreeCAD
**Manual Installation:**
Copy the `FreecadRobustMCPBridge` folder to your FreeCAD Mod directory:
- **macOS**: `~/Library/Application Support/FreeCAD/Mod/`
- **Linux**: `~/.local/share/FreeCAD/Mod/`
- **Windows**: `%APPDATA%/FreeCAD/Mod/`
### Upgrade Notes
- **Uninstall the old macro**: If you had `StartMCPBridge.FCMacro`, you can delete it
- **Auto-start disabled by default**: Enable in Preferences if you want the bridge to start automatically
- **Same ports**: Default ports remain 9875 (XML-RPC) and 9876 (JSON-RPC Socket)
+6 -5
View File
@@ -181,12 +181,13 @@ _result_ = {{"success": True}}
Releases are automated via GitHub Actions:
1. Update `CHANGELOG.md`
1. Create a GitHub Release with a version tag
1. Update the component's `RELEASE_NOTES.md` file (see [Releasing](releasing.md) for details)
1. Bump versions for workbench/macros (MCP Server auto-bumps from tag)
1. Commit and push changes
1. Create release tag with `just release::tag-<component> X.Y.Z`
1. CI builds and publishes:
- PyPI package
- Docker images
- Macro release archives
- PyPI package and Docker images (MCP Server)
- GitHub Release archives (workbench and macros)
---
+31 -29
View File
@@ -12,9 +12,9 @@ just release::status # Check which components have unrele
just release::changes-since mcp-server # View specific changes (or workbench, macro-magnets, macro-export)
just all # Run all quality checks (must pass)
# 2. Update changelog
# 2. Update release notes
just release::draft-notes mcp-server # Generate draft notes from commits
# Then manually edit CHANGELOG.md with appropriate section header
# Then edit the component's RELEASE_NOTES.md file (see "Release Notes Files" below)
# 3. Version bump (workbench & macros only - MCP Server uses setuptools-scm)
just release::bump-workbench 1.0.0 # or bump-macro-magnets, bump-macro-export
@@ -130,9 +130,18 @@ For a more thorough check including integration tests:
just all-with-integration
```
### 4. Update CHANGELOG.md
### 4. Update Release Notes
The changelog uses a multi-component structure. Each release date section contains entries for each component that was released.
Each component has its own `RELEASE_NOTES.md` file. Release workflows automatically extract the relevant section for GitHub Releases.
#### Release Notes Files
| Component | Release Notes File |
| ---------------------------- | ----------------------------------------------------- |
| MCP Server | `src/freecad_mcp/RELEASE_NOTES.md` |
| Robust MCP Bridge Workbench | `addon/FreecadRobustMCPBridge/RELEASE_NOTES.md` |
| Cut Object for Magnets Macro | `macros/Cut_Object_for_Magnets/RELEASE_NOTES.md` |
| Multi Export Macro | `macros/Multi_Export/RELEASE_NOTES.md` |
#### Draft Release Notes
@@ -146,43 +155,36 @@ just release::draft-notes macro-magnets
just release::draft-notes macro-export
```
This categorizes commits by type (feat, fix, refactor, etc.) to help you write the changelog entry.
This categorizes commits by type (feat, fix, refactor, etc.) to help you write the release notes.
#### Changelog Structure
#### Release Notes Format
Add entries under the appropriate component section in `CHANGELOG.md`:
Add a new version section **at the top** of the component's `RELEASE_NOTES.md`:
```markdown
## YYYY-MM-DD - Release Title (optional)
## Version X.Y.Z (YYYY-MM-DD)
### MCP Server vX.Y.Z
Release notes for changes between vA.B.C and vX.Y.Z.
Brief description of the release.
### Added
#### Added
- New feature description
#### Changed
### Changed
- Changed behavior description
#### Fixed
### Fixed
- Bug fix description
---
### Robust MCP Bridge Workbench vX.Y.Z
...
```
**Important:** The changelog section header format must match exactly for the GitHub Release workflow to extract it:
**Important:** The version header format must match exactly for the GitHub Release workflow to extract it:
- MCP Server: `### MCP Server vX.Y.Z`
- Workbench: `### Robust MCP Bridge Workbench vX.Y.Z`
- Magnets Macro: `### Cut Object for Magnets Macro vX.Y.Z`
- Export Macro: `### Multi Export Macro vX.Y.Z`
- Format: `## Version X.Y.Z (YYYY-MM-DD)`
- Example: `## Version 1.0.0 (2026-01-15)`
The release workflows automatically extract the changelog section and include it in the GitHub Release body.
The release workflows automatically extract the version section and include it in the GitHub Release body.
## Releasing Each Component
@@ -194,9 +196,9 @@ The MCP Server is the main Python package. It uses `setuptools-scm` to derive ve
# 1. Ensure all changes are committed
git status # Should show clean working tree
# 2. Update CHANGELOG.md and commit
git add CHANGELOG.md
git commit -m "docs: update changelog for MCP Server v1.0.0"
# 2. Update src/freecad_mcp/RELEASE_NOTES.md and commit
git add src/freecad_mcp/RELEASE_NOTES.md
git commit -m "docs: update release notes for MCP Server v1.0.0"
# 3. Create and push the release tag
just release::tag-mcp-server 1.0.0
@@ -474,7 +476,7 @@ just release::status
# View changes for a component
just release::changes-since mcp-server
# Draft changelog entries from commits
# Draft release notes from commits
just release::draft-notes mcp-server
just release::draft-notes workbench
just release::draft-notes macro-magnets
+19 -15
View File
@@ -1075,24 +1075,25 @@ draft-notes component:
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 release notes section for a specific component version (for GitHub Release body)
# Reads from component-specific RELEASE_NOTES.md files
extract-changelog component version:
#!/usr/bin/env bash
set -euo pipefail
# Match header exactly as it appears in CHANGELOG.md
# Map component to RELEASE_NOTES.md path
case "{{component}}" in
mcp-server|server)
HEADER="### Robust MCP Server v{{version}}"
RELEASE_NOTES="{{project_root}}/src/freecad_mcp/RELEASE_NOTES.md"
;;
workbench)
HEADER="### Robust MCP Bridge Workbench v{{version}}"
RELEASE_NOTES="{{project_root}}/addon/FreecadRobustMCPBridge/RELEASE_NOTES.md"
;;
macro-magnets|magnets)
HEADER="### Cut Object for Magnets Macro v{{version}}"
RELEASE_NOTES="{{project_root}}/macros/Cut_Object_for_Magnets/RELEASE_NOTES.md"
;;
macro-export|export)
HEADER="### Multi Export Macro v{{version}}"
RELEASE_NOTES="{{project_root}}/macros/Multi_Export/RELEASE_NOTES.md"
;;
*)
echo "Unknown component: {{component}}"
@@ -1100,18 +1101,21 @@ extract-changelog component version:
;;
esac
CHANGELOG="{{project_root}}/CHANGELOG.md"
if [ ! -f "$RELEASE_NOTES" ]; then
echo "No RELEASE_NOTES.md found at: $RELEASE_NOTES"
exit 0
fi
# 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" '
# Extract section for this version
# Format: ## Version X.Y.Z (date)
awk -v version="{{version}}" '
BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next }
found && /^---$/ { exit }
found && /^### [A-Z]/ { exit }
/^## Version / {
if (found) exit
if (index($0, version) > 0) { found=1; next }
}
found { print }
' "$CHANGELOG"
' "$RELEASE_NOTES"
# =============================================================================
# Dry Run Commands (preview without pushing)
+38 -10
View File
@@ -305,19 +305,47 @@ release-test:
if [ "$TESTS_PASSED" = true ]; then
echo "✓ ALL TESTS PASSED - Ready for release!"
echo ""
echo "NOTE: There is no need to bump the MCP server version as it happens automatically from the release tag on git."
echo "============================================================"
echo " NEXT STEPS FOR RELEASE"
echo "============================================================"
echo ""
echo "You can now create a release with:"
echo " just release::bump-workbench <version>"
echo " just release::bump-macro-magnets <version>"
echo " just release::bump-macro-export <version>"
echo "1. UPDATE RELEASE NOTES (for each component you're releasing):"
echo ""
echo "NOTE: Merge all code/PRs into 'main' that are in flight for the release, like the version bumps above!"
echo " Edit the RELEASE_NOTES.md file for each component:"
echo " - MCP Server: src/freecad_mcp/RELEASE_NOTES.md"
echo " - Workbench: addon/FreecadRobustMCPBridge/RELEASE_NOTES.md"
echo " - Cut Macro: macros/Cut_Object_for_Magnets/RELEASE_NOTES.md"
echo " - Export Macro: macros/Multi_Export/RELEASE_NOTES.md"
echo ""
echo " just release::tag-mcp-server <version>"
echo " just release::tag-workbench <version>"
echo " just release::tag-macro-magnets <version>"
echo " just release::tag-macro-export <version>"
echo " Add a new version section at the top:"
echo " ## Version X.Y.Z (YYYY-MM-DD)"
echo " ### Added"
echo " - New feature description"
echo " ### Changed"
echo " - Change description"
echo " ### Fixed"
echo " - Bug fix description"
echo ""
echo " Tip: Use 'just release::draft-notes <component>' to generate draft notes"
echo " from git commits since the last release."
echo ""
echo "2. BUMP VERSIONS (for workbench/macros only - MCP server auto-bumps from tag):"
echo ""
echo " just release::bump-workbench <version>"
echo " just release::bump-macro-magnets <version>"
echo " just release::bump-macro-export <version>"
echo ""
echo "3. COMMIT AND PUSH your RELEASE_NOTES.md and version bump changes"
echo ""
echo "4. CREATE RELEASE TAGS:"
echo ""
echo " just release::tag-mcp-server <version>"
echo " just release::tag-workbench <version>"
echo " just release::tag-macro-magnets <version>"
echo " just release::tag-macro-export <version>"
echo ""
echo "NOTE: The MCP server version is determined by the git tag, so no bump needed."
echo "NOTE: Release workflows extract notes from each component's RELEASE_NOTES.md."
else
echo "✗ SOME TESTS FAILED - Cannot proceed with release"
echo ""
@@ -0,0 +1,42 @@
# Cut Object for Magnets Macro Release Notes
## Version 0.6.1 (2026-01-12)
Release notes for changes between v0.5.0-beta and v0.6.1.
### Added
- **FreeCAD Addon Manager metadata**: Macro now includes standard metadata fields for better Addon Manager integration
- **FreeCAD Wiki page**: Official wiki documentation at [Macro Cut Object for Magnets](https://wiki.freecad.org/Macro_Cut_Object_for_Magnets)
- **Example images**: Added screenshots showing the dialog and example output
### Changed
- **Version tracking**: Version now managed via `__Version__` metadata field instead of inline comment
### Fixed
- **Ring-shaped objects**: Improved handling of inset point calculation for hollow/ring-shaped cut faces where the inset point could land in the hole
- **Code formatting**: Minor cleanup for consistent code style
### No Functional Changes
The core cutting algorithm, magnet hole placement, and collision detection remain unchanged from the beta release. This macro is stable for production use.
### Installation
**Via FreeCAD Addon Manager:**
1. Open FreeCAD
2. Go to Macro > Macros...
3. Click "Download" tab
4. Search for "Cut Object for Magnets"
5. Click Install
**Manual Installation:**
Copy `CutObjectForMagnets.FCMacro` to your FreeCAD macro directory:
- **macOS**: `~/Library/Application Support/FreeCAD/Macro/`
- **Linux**: `~/.local/share/FreeCAD/Macro/`
- **Windows**: `%APPDATA%/FreeCAD/Macro/`
+45
View File
@@ -0,0 +1,45 @@
# Multi Export Macro Release Notes
## Version 0.6.1 (2026-01-12)
Release notes for changes between v0.5.0-beta and v0.6.1.
### Added
- **FreeCAD Addon Manager metadata**: Macro now includes standard metadata fields for better Addon Manager integration
- **FreeCAD Wiki page**: Official wiki documentation at [Macro Multi Export](https://wiki.freecad.org/Macro_Multi_Export)
- **Dialog screenshot**: Added screenshot showing the export dialog
### Changed
- **Minimum FreeCAD version**: Updated requirement from FreeCAD 0.19 to FreeCAD 0.21 (for better 3MF support)
- **Version tracking**: Version now managed via `__Version__` metadata field instead of inline comment
### No Functional Changes
The core export functionality remains unchanged from the beta release:
- Multi-format export (STL, STEP, 3MF, OBJ, IGES, BREP, PLY, AMF)
- Configurable mesh tolerance for tessellated formats
- Batch export with progress feedback
- Format-specific options
This macro is stable for production use.
### Installation
**Via FreeCAD Addon Manager:**
1. Open FreeCAD
2. Go to Macro > Macros...
3. Click "Download" tab
4. Search for "Multi Export"
5. Click Install
**Manual Installation:**
Copy `MultiExport.FCMacro` to your FreeCAD macro directory:
- **macOS**: `~/Library/Application Support/FreeCAD/Macro/`
- **Linux**: `~/.local/share/FreeCAD/Macro/`
- **Windows**: `%APPDATA%/FreeCAD/Macro/`
+1 -1
View File
@@ -102,7 +102,7 @@ Homepage = "https://github.com/spkane/freecad-robust-mcp-and-more"
Documentation = "https://github.com/spkane/freecad-robust-mcp-and-more#readme"
Repository = "https://github.com/spkane/freecad-robust-mcp-and-more.git"
Issues = "https://github.com/spkane/freecad-robust-mcp-and-more/issues"
Changelog = "https://github.com/spkane/freecad-robust-mcp-and-more/blob/main/CHANGELOG.md"
"Release Notes" = "https://github.com/spkane/freecad-robust-mcp-and-more/blob/main/src/freecad_mcp/RELEASE_NOTES.md"
"Docker Hub" = "https://hub.docker.com/r/spkane/freecad-robust-mcp"
[tool.hatch.version]
+50
View File
@@ -0,0 +1,50 @@
# Robust MCP Server Release Notes
## Version 0.6.1 (2026-01-12)
Release notes for changes between v0.5.0-beta and v0.6.1.
### Added
- **Instance ID tracking**: Each server instance now has a unique UUID for identifying connections
- **Lifespan management**: Proper async lifecycle handling for bridge connections
- **Enhanced logging**: Improved startup logging with connection mode, transport type, and FreeCAD version
- **Connection validation**: Server validates FreeCAD bridge connection at startup with clear error messages
### Changed
- **Renamed from "FreeCAD MCP" to "Robust MCP Server"**: Reflects the broader scope and stability improvements
- **Bridge architecture refactored**: Cleaner separation between MCP server and FreeCAD bridge components
- **Plugin code moved to workbench**: The embedded FreeCAD plugin (`freecad_plugin/`) is now part of the separate Robust MCP Bridge Workbench addon
- **Simplified tool registration**: Tools now use consistent async patterns with typed callbacks
- **Export tools improved**: Better error handling and format validation for STEP, STL, 3MF, OBJ, IGES exports
### Removed
- **Docker detection fields**: Removed `in_docker` and `docker_container_id` from `get_mcp_server_environment()` tool (unreliable detection)
- **Embedded plugin code**: The `freecad_plugin/` directory moved to the workbench addon for cleaner separation
### Fixed
- **XML-RPC connection handling**: Improved timeout and retry logic for bridge connections
- **Console output capture**: More reliable capture of FreeCAD console messages
- **Type annotations**: Added proper type hints throughout for better IDE support
### Installation
```bash
# PyPI (recommended)
pip install freecad-robust-mcp
# Or with uv
uv tool install freecad-robust-mcp
# Docker (use 'latest' or a specific version tag)
docker pull spkane/freecad-robust-mcp:latest
```
### Upgrade Notes
- The server now requires the **Robust MCP Bridge Workbench** to be installed in FreeCAD (replaces the old Start MCP Bridge macro)
- Environment variable names unchanged (`FREECAD_MODE`, `FREECAD_SOCKET_HOST`, etc.)
- All existing MCP tools remain compatible
Generated
+9 -9
View File
@@ -561,11 +561,11 @@ wheels = [
[[package]]
name = "filelock"
version = "3.20.2"
version = "3.20.3"
source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" }
sdist = { url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510, upload-time = "2026-01-02T15:33:32.582Z" }
sdist = { url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" }
wheels = [
{ url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" },
{ url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" },
]
[[package]]
@@ -833,14 +833,14 @@ wheels = [
[[package]]
name = "jaraco-context"
version = "6.0.2"
version = "6.1.0"
source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" }
dependencies = [
{ name = "backports-tarfile", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/8d/7d/41acf8e22d791bde812cb6c2c36128bb932ed8ae066bcb5e39cb198e8253/jaraco_context-6.0.2.tar.gz", hash = "sha256:953ae8dddb57b1d791bf72ea1009b32088840a7dd19b9ba16443f62be919ee57", size = 14994, upload-time = "2025-12-24T19:21:35.784Z" }
sdist = { url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" }
wheels = [
{ url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/c7/0c/1e0096ced9c55f9c6c6655446798df74165780375d3f5ab5f33751e087ae/jaraco_context-6.0.2-py3-none-any.whl", hash = "sha256:55fc21af4b4f9ca94aa643b6ee7fe13b1e4c01abf3aeb98ca4ad9c80b741c786", size = 6988, upload-time = "2025-12-24T19:21:34.557Z" },
{ url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" },
]
[[package]]
@@ -2633,16 +2633,16 @@ wheels = [
[[package]]
name = "virtualenv"
version = "20.36.0"
version = "20.36.1"
source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" }
dependencies = [
{ name = "distlib" },
{ name = "filelock" },
{ name = "platformdirs" },
]
sdist = { url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/78/49/87e23d8f742f10f965bce5d6b285fc88a4f436b11daf6b6225d4d66f8492/virtualenv-20.36.0.tar.gz", hash = "sha256:a3601f540b515a7983508113f14e78993841adc3d83710fa70f0ac50f43b23ed", size = 6032237, upload-time = "2026-01-07T17:20:04.975Z" }
sdist = { url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" }
wheels = [
{ url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/eb/6a/0af36875e0023a1f2d0b66b4051721fc26740e947696922df1665b75e5d3/virtualenv-20.36.0-py3-none-any.whl", hash = "sha256:e7ded577f3af534fd0886d4ca03277f5542053bedb98a70a989d3c22cfa5c9ac", size = 6008261, upload-time = "2026-01-07T17:20:02.87Z" },
{ url = "https://pkgs.safetycli.com/package/techlabssh/pypi/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" },
]
[[package]]