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 .codespell-ignore-words.txt
mkdocs.yml mkdocs.yml
ARCHITECTURE-MCP.md ARCHITECTURE-MCP.md
CHANGELOG.md
CLAUDE.md CLAUDE.md
**/RELEASE_NOTES.md
+7 -2
View File
@@ -11,6 +11,7 @@ on:
- ".dockerignore" - ".dockerignore"
- "src/**/*.py" - "src/**/*.py"
- "pyproject.toml" - "pyproject.toml"
- "uv.lock"
- ".github/workflows/docker.yaml" - ".github/workflows/docker.yaml"
pull_request: pull_request:
branches: [main, master] branches: [main, master]
@@ -19,6 +20,7 @@ on:
- ".dockerignore" - ".dockerignore"
- "src/**/*.py" - "src/**/*.py"
- "pyproject.toml" - "pyproject.toml"
- "uv.lock"
- ".github/workflows/docker.yaml" - ".github/workflows/docker.yaml"
workflow_dispatch: workflow_dispatch:
workflow_call: workflow_call:
@@ -106,8 +108,8 @@ jobs:
- name: Test Docker image - name: Test Docker image
run: | run: |
# Build for current platform only for testing # Build for current platform only for testing (--no-cache ensures fresh build)
docker build --build-arg VERSION=${{ steps.version.outputs.VERSION }} -t ${{ env.IMAGE_NAME }}:test . 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 # 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"}}}' | \ 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 uses: aquasecurity/trivy-action@0.33.1
with: with:
image-ref: ${{ env.IMAGE_NAME }}:test image-ref: ${{ env.IMAGE_NAME }}:test
version: "v0.68.2"
severity: "HIGH,CRITICAL" severity: "HIGH,CRITICAL"
exit-code: "1" exit-code: "1"
format: "table" format: "table"
@@ -139,6 +142,7 @@ jobs:
uses: aquasecurity/trivy-action@0.33.1 uses: aquasecurity/trivy-action@0.33.1
with: with:
image-ref: ${{ env.IMAGE_NAME }}:test image-ref: ${{ env.IMAGE_NAME }}:test
version: "v0.68.2"
severity: "MEDIUM,LOW" severity: "MEDIUM,LOW"
exit-code: "0" exit-code: "0"
format: "table" format: "table"
@@ -151,6 +155,7 @@ jobs:
uses: aquasecurity/trivy-action@0.33.1 uses: aquasecurity/trivy-action@0.33.1
with: with:
image-ref: ${{ env.IMAGE_NAME }}:test image-ref: ${{ env.IMAGE_NAME }}:test
version: "v0.68.2"
format: "sarif" format: "sarif"
output: "trivy-results.sarif" output: "trivy-results.sarif"
cache-dir: .trivy-cache cache-dir: .trivy-cache
+10 -11
View File
@@ -222,7 +222,7 @@ jobs:
echo "Created archives:" echo "Created archives:"
ls -la "${ARCHIVE_NAME}-${VERSION}."* ls -la "${ARCHIVE_NAME}-${VERSION}."*
- name: Extract changelog section - name: Extract release notes section
id: changelog id: changelog
env: env:
VERSION: ${{ steps.version.outputs.version }} VERSION: ${{ steps.version.outputs.version }}
@@ -233,19 +233,18 @@ jobs:
README_FILE: ${{ inputs.readme_file }} README_FILE: ${{ inputs.readme_file }}
WIKI_URL: ${{ inputs.wiki_url }} WIKI_URL: ${{ inputs.wiki_url }}
run: | run: |
# Match header exactly as it appears in CHANGELOG.md RELEASE_NOTES="macros/${MACRO_DIR}/RELEASE_NOTES.md"
HEADER="### ${MACRO_NAME} Macro v${VERSION}"
# Extract section between this version header and the next component header or separator # Extract section for this version from RELEASE_NOTES.md
# Only exit on: "---" separator OR "### " followed by component name (capital letter) # Format: ## Version X.Y.Z (date)
# This allows #### Added, #### Changed, etc. to be included CHANGELOG_CONTENT=$(awk -v version="$VERSION" '
CHANGELOG_CONTENT=$(awk -v header="$HEADER" '
BEGIN { found=0 } BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next } /^## Version / {
found && /^---$/ { exit } if (found) exit
found && /^### [A-Z]/ { exit } if (index($0, version) > 0) { found=1; next }
}
found { print } found { print }
' CHANGELOG.md) ' "$RELEASE_NOTES" 2>/dev/null || echo "")
# Build release body # Build release body
cat > release_body.md << EOF cat > release_body.md << EOF
+13 -13
View File
@@ -347,23 +347,23 @@ jobs:
name: python-package-distributions name: python-package-distributions
path: dist/ path: dist/
- name: Extract changelog section - name: Extract release notes section
id: changelog id: changelog
run: | run: |
VERSION="${{ needs.validate-tag.outputs.version }}" VERSION="${{ needs.validate-tag.outputs.version }}"
# Match header exactly as it appears in CHANGELOG.md RELEASE_NOTES="src/freecad_mcp/RELEASE_NOTES.md"
HEADER="### Robust MCP Server v${VERSION}"
# Extract section between this version header and the next component header or separator # Extract section for this version from RELEASE_NOTES.md
# Only exit on: "---" separator OR "### " followed by component name (capital letter) # Format: ## Version X.Y.Z (date)
# This allows #### Added, #### Changed, etc. to be included # Extract everything between this version header and the next ## Version header
CHANGELOG_CONTENT=$(awk -v header="$HEADER" ' CHANGELOG_CONTENT=$(awk -v version="$VERSION" '
BEGIN { found=0 } BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next } /^## Version / {
found && /^---$/ { exit } if (found) exit
found && /^### [A-Z]/ { exit } if (index($0, version) > 0) { found=1; next }
}
found { print } found { print }
' CHANGELOG.md) ' "$RELEASE_NOTES")
# Write to file for the release body (handles multiline) # Write to file for the release body (handles multiline)
echo "$CHANGELOG_CONTENT" > changelog_section.md echo "$CHANGELOG_CONTENT" > changelog_section.md
@@ -371,10 +371,10 @@ jobs:
# Check if we got content # Check if we got content
if [ -n "$CHANGELOG_CONTENT" ]; then if [ -n "$CHANGELOG_CONTENT" ]; then
echo "found=true" >> "$GITHUB_OUTPUT" echo "found=true" >> "$GITHUB_OUTPUT"
echo "Extracted changelog section for $HEADER" echo "Extracted release notes for version $VERSION"
else else
echo "found=false" >> "$GITHUB_OUTPUT" 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 fi
- name: Create GitHub Release - name: Create GitHub Release
+13 -11
View File
@@ -162,23 +162,25 @@ jobs:
echo "Created archives:" echo "Created archives:"
ls -la "freecad-mcp-workbench-${VERSION}."* ls -la "freecad-mcp-workbench-${VERSION}."*
- name: Extract changelog section - name: Extract release notes section
id: changelog id: changelog
run: | run: |
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
# Match header exactly as it appears in CHANGELOG.md (with optional space before v) RELEASE_NOTES="addon/FreecadRobustMCPBridge/RELEASE_NOTES.md"
HEADER="### Robust MCP Bridge Workbench v${VERSION}"
# Extract section between this version header and the next component header or separator # Extract section for this version from RELEASE_NOTES.md
# Only exit on: "---" separator OR "### " followed by component name (capital letter) # Format: ## Version X.Y.Z (date)
# This allows #### Added, #### Changed, etc. to be included # Use exact version match to avoid substring issues (e.g., 1.0.0 matching 1.0.0-alpha)
CHANGELOG_CONTENT=$(awk -v header="$HEADER" ' CHANGELOG_CONTENT=$(awk -v version="$VERSION" '
BEGIN { found=0 } BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next } /^## Version / {
found && /^---$/ { exit } if (found) exit
found && /^### [A-Z]/ { exit } # Extract version field (3rd word) and compare exactly
split($0, parts, " ")
if (parts[3] == version) { found=1; next }
}
found { print } found { print }
' CHANGELOG.md) ' "$RELEASE_NOTES" 2>/dev/null || echo "")
# Build release body with changelog content if available # Build release body with changelog content if available
cat > release_body.md << 'STATIC_EOF' 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 github-cli = "2.74" # GitHub CLI for PR/issue management
# Security and code quality tools # Security and code quality tools
trivy = "0.62" # container vulnerability scanner trivy = "0.68" # container vulnerability scanner
gitleaks = "8.30" # secrets scanner gitleaks = "8.30" # secrets scanner
actionlint = "1.7" # GitHub Actions linter actionlint = "1.7" # GitHub Actions linter
# Note: hadolint and shellcheck are managed by their pre-commit repos # Note: hadolint and shellcheck are managed by their pre-commit repos
+3 -3
View File
@@ -1,5 +1,5 @@
[project] [project]
id = freecad-mcp-and-more id = freecad-addon-robust-mcp-server
url = /codebases/freecad-mcp-and-more/findings url = /codebases/freecad-addon-robust-mcp-server/findings
name = freecad-mcp-and-more name = freecad-addon-robust-mcp-server
+1 -1
View File
@@ -92,7 +92,7 @@
}, },
{ {
"path": "detect_secrets.filters.common.is_baseline_file", "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", "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 ├── .pre-commit-config.yaml # Pre-commit hook configuration
├── .safety-policy.yml # Safety CLI scan policy ├── .safety-policy.yml # Safety CLI scan policy
├── .secrets.baseline # detect-secrets baseline ├── .secrets.baseline # detect-secrets baseline
├── CHANGELOG.md # Project changelog
├── CLAUDE.md # This file (AI assistant guidelines) ├── CLAUDE.md # This file (AI assistant guidelines)
├── Dockerfile # Docker image definition ├── Dockerfile # Docker image definition
├── justfile # Main task runner (imports modules) ├── 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. *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 ```bash
just release::draft-notes mcp-server 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 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 ```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 ### Added
- New feature
--- - 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 ### Creating a Release
Use the `just release::` commands to create and push release tags: **Step 1: Run release tests** to ensure everything passes:
```bash ```bash
# Check what has unreleased changes just testing::release-test
just release::status ```
# Preview changes since last release This runs unit tests, integration tests (headless + GUI), Docker tests, and just command tests. All must pass before proceeding.
just release::changes-since mcp-server
just release::changes-since workbench
**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) # Release the MCP server (triggers PyPI, Docker, GitHub release)
just release::tag-mcp-server 1.0.0 just release::tag-mcp-server 1.0.0
@@ -1427,10 +1460,23 @@ just release::tag-workbench 1.0.0
# Release macros # Release macros
just release::tag-macro-magnets 1.0.0 just release::tag-macro-magnets 1.0.0
just release::tag-macro-export 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 # View release tags
just release::list-tags just release::list-tags
just release::latest-versions just release::latest-versions
# Extract changelog for a version (for testing)
just release::extract-changelog mcp-server 1.0.0
``` ```
### Version Format ### 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 **When preparing a release:**
- Group changes under: Added, Changed, Deprecated, Removed, Fixed, Security
- Include date for each release version 1. Use `just release::draft-notes <component>` to generate draft notes from commits
- Document all significant changes, not just code changes 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 - [ ] Run `just all` before finishing - everything must pass
- [ ] Use latest stable library versions - [ ] Use latest stable library versions
- [ ] Follow security best practices - [ ] 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 pip install --no-cache-dir --no-compile uv
# Copy only dependency files first for better layer caching # 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/ COPY src/ ./src/
# Version for setuptools-scm when building without git (e.g., in Docker) # 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 ARG VERSION=0.0.0.dev0
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${VERSION} 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 # Using uv cache mount for faster rebuilds
# --frozen ensures uv.lock is used exactly without updates
RUN --mount=type=cache,target=/root/.cache/uv \ RUN --mount=type=cache,target=/root/.cache/uv \
uv venv /opt/venv && \ uv venv /opt/venv && \
. /opt/venv/bin/activate && \ UV_PROJECT_ENVIRONMENT=/opt/venv uv sync --frozen --no-dev --no-editable
uv pip install --no-compile .
# ============================================================================= # =============================================================================
# Stage 2: Runtime - Minimal image for running the server # Stage 2: Runtime - Minimal image for running the server
@@ -77,12 +78,17 @@ RUN apk upgrade --no-cache
RUN addgroup -g 1000 mcpuser && \ RUN addgroup -g 1000 mcpuser && \
adduser -u 1000 -G mcpuser -s /bin/sh -D mcpuser adduser -u 1000 -G mcpuser -s /bin/sh -D mcpuser
# Upgrade system pip to fix CVE-2025-8869 (defense-in-depth) # Remove pip, setuptools, and wheel from system Python to fix CVEs
# Note: Although PATH prefers /opt/venv/bin, we upgrade the system pip at # - The base image has pip with CVE-2025-8869
# /usr/local/bin/pip intentionally. This ensures no vulnerable pip exists # - setuptools vendors jaraco.context 5.3.0 with GHSA-58pv-8j8x-9vj2
# in the image, even if the venv is bypassed or pip is invoked directly. # 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 # 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 virtual environment from builder
COPY --from=builder /opt/venv /opt/venv 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: Releases are automated via GitHub Actions:
1. Update `CHANGELOG.md` 1. Update the component's `RELEASE_NOTES.md` file (see [Releasing](releasing.md) for details)
1. Create a GitHub Release with a version tag 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: 1. CI builds and publishes:
- PyPI package - PyPI package and Docker images (MCP Server)
- Docker images - GitHub Release archives (workbench and macros)
- Macro release archives
--- ---
+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 release::changes-since mcp-server # View specific changes (or workbench, macro-magnets, macro-export)
just all # Run all quality checks (must pass) 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 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) # 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 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 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 #### Draft Release Notes
@@ -146,43 +155,36 @@ just release::draft-notes macro-magnets
just release::draft-notes macro-export 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 ```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 - New feature description
#### Changed ### Changed
- Changed behavior description - Changed behavior description
#### Fixed ### Fixed
- Bug fix description - 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` - Format: `## Version X.Y.Z (YYYY-MM-DD)`
- Workbench: `### Robust MCP Bridge Workbench vX.Y.Z` - Example: `## Version 1.0.0 (2026-01-15)`
- Magnets Macro: `### Cut Object for Magnets Macro vX.Y.Z`
- Export Macro: `### Multi Export Macro vX.Y.Z`
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 ## 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 # 1. Ensure all changes are committed
git status # Should show clean working tree git status # Should show clean working tree
# 2. Update CHANGELOG.md and commit # 2. Update src/freecad_mcp/RELEASE_NOTES.md and commit
git add CHANGELOG.md git add src/freecad_mcp/RELEASE_NOTES.md
git commit -m "docs: update changelog for MCP Server v1.0.0" git commit -m "docs: update release notes for MCP Server v1.0.0"
# 3. Create and push the release tag # 3. Create and push the release tag
just release::tag-mcp-server 1.0.0 just release::tag-mcp-server 1.0.0
@@ -474,7 +476,7 @@ just release::status
# View changes for a component # View changes for a component
just release::changes-since mcp-server 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 mcp-server
just release::draft-notes workbench just release::draft-notes workbench
just release::draft-notes macro-magnets just release::draft-notes macro-magnets
+19 -15
View File
@@ -1075,24 +1075,25 @@ draft-notes component:
echo "" echo ""
git log --oneline "$REV_RANGE" -- $PATHS 2>/dev/null | sed -E 's/^[a-f0-9]+ /- /' || echo "(no commits)" 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: extract-changelog component version:
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# Match header exactly as it appears in CHANGELOG.md # Map component to RELEASE_NOTES.md path
case "{{component}}" in case "{{component}}" in
mcp-server|server) mcp-server|server)
HEADER="### Robust MCP Server v{{version}}" RELEASE_NOTES="{{project_root}}/src/freecad_mcp/RELEASE_NOTES.md"
;; ;;
workbench) workbench)
HEADER="### Robust MCP Bridge Workbench v{{version}}" RELEASE_NOTES="{{project_root}}/addon/FreecadRobustMCPBridge/RELEASE_NOTES.md"
;; ;;
macro-magnets|magnets) 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) macro-export|export)
HEADER="### Multi Export Macro v{{version}}" RELEASE_NOTES="{{project_root}}/macros/Multi_Export/RELEASE_NOTES.md"
;; ;;
*) *)
echo "Unknown component: {{component}}" echo "Unknown component: {{component}}"
@@ -1100,18 +1101,21 @@ extract-changelog component version:
;; ;;
esac 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 # Extract section for this version
# Only exit on: "---" separator OR "### " followed by component name (capital letter) # Format: ## Version X.Y.Z (date)
# This allows #### Added, #### Changed, etc. to be included awk -v version="{{version}}" '
awk -v header="$HEADER" '
BEGIN { found=0 } BEGIN { found=0 }
$0 == header || $0 == header " " { found=1; next } /^## Version / {
found && /^---$/ { exit } if (found) exit
found && /^### [A-Z]/ { exit } if (index($0, version) > 0) { found=1; next }
}
found { print } found { print }
' "$CHANGELOG" ' "$RELEASE_NOTES"
# ============================================================================= # =============================================================================
# Dry Run Commands (preview without pushing) # Dry Run Commands (preview without pushing)
+31 -3
View File
@@ -305,19 +305,47 @@ release-test:
if [ "$TESTS_PASSED" = true ]; then if [ "$TESTS_PASSED" = true ]; then
echo "✓ ALL TESTS PASSED - Ready for release!" echo "✓ ALL TESTS PASSED - Ready for release!"
echo "" 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 "1. UPDATE RELEASE NOTES (for each component you're releasing):"
echo ""
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 " 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 ""
echo "You can now create a release with:"
echo " just release::bump-workbench <version>" echo " just release::bump-workbench <version>"
echo " just release::bump-macro-magnets <version>" echo " just release::bump-macro-magnets <version>"
echo " just release::bump-macro-export <version>" echo " just release::bump-macro-export <version>"
echo "" echo ""
echo "NOTE: Merge all code/PRs into 'main' that are in flight for the release, like the version bumps above!" echo "3. COMMIT AND PUSH your RELEASE_NOTES.md and version bump changes"
echo ""
echo "4. CREATE RELEASE TAGS:"
echo "" echo ""
echo " just release::tag-mcp-server <version>" echo " just release::tag-mcp-server <version>"
echo " just release::tag-workbench <version>" echo " just release::tag-workbench <version>"
echo " just release::tag-macro-magnets <version>" echo " just release::tag-macro-magnets <version>"
echo " just release::tag-macro-export <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 else
echo "✗ SOME TESTS FAILED - Cannot proceed with release" echo "✗ SOME TESTS FAILED - Cannot proceed with release"
echo "" 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" Documentation = "https://github.com/spkane/freecad-robust-mcp-and-more#readme"
Repository = "https://github.com/spkane/freecad-robust-mcp-and-more.git" Repository = "https://github.com/spkane/freecad-robust-mcp-and-more.git"
Issues = "https://github.com/spkane/freecad-robust-mcp-and-more/issues" 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" "Docker Hub" = "https://hub.docker.com/r/spkane/freecad-robust-mcp"
[tool.hatch.version] [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]] [[package]]
name = "filelock" name = "filelock"
version = "3.20.2" version = "3.20.3"
source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" } 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 = [ 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]] [[package]]
@@ -833,14 +833,14 @@ wheels = [
[[package]] [[package]]
name = "jaraco-context" name = "jaraco-context"
version = "6.0.2" version = "6.1.0"
source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" } source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" }
dependencies = [ dependencies = [
{ name = "backports-tarfile", marker = "python_full_version < '3.12'" }, { 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 = [ 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]] [[package]]
@@ -2633,16 +2633,16 @@ wheels = [
[[package]] [[package]]
name = "virtualenv" name = "virtualenv"
version = "20.36.0" version = "20.36.1"
source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" } source = { registry = "https://pkgs.safetycli.com/repository/techlabssh/pypi/simple/" }
dependencies = [ dependencies = [
{ name = "distlib" }, { name = "distlib" },
{ name = "filelock" }, { name = "filelock" },
{ name = "platformdirs" }, { 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 = [ 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]] [[package]]