ci: Various Linux container and PyPi release improvements (#16)
* ci: release workflow fixes * docs: Fix env var names * docs: Improve Docker network configuration * ci: Improve error checking in Docker release workflow * ci: improve release workflow and container security * ci: badges and better container scanning * ci: Addition fixes * ci: fix pypi release logic
This commit is contained in:
@@ -113,13 +113,42 @@ jobs:
|
||||
${{ env.IMAGE_NAME }}:test 2>&1 | \
|
||||
grep -q '"result"' && echo "Container test passed" || echo "Container test completed"
|
||||
|
||||
- name: Scan for vulnerabilities
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: aquasecurity/trivy-action@master
|
||||
- name: Cache Trivy vulnerability database
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
image-ref: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||
path: .trivy-cache
|
||||
# Cache key based on OS and workflow file hash; refreshes when workflow changes
|
||||
key: trivy-db-${{ runner.os }}-${{ hashFiles('.github/workflows/docker.yaml') }}
|
||||
restore-keys: |
|
||||
trivy-db-${{ runner.os }}-
|
||||
|
||||
- name: Scan for HIGH/CRITICAL vulnerabilities (fail build)
|
||||
uses: aquasecurity/trivy-action@0.33.1
|
||||
with:
|
||||
image-ref: ${{ env.IMAGE_NAME }}:test
|
||||
severity: "HIGH,CRITICAL"
|
||||
exit-code: "1"
|
||||
format: "table"
|
||||
cache-dir: .trivy-cache
|
||||
|
||||
- name: Scan for MEDIUM/LOW vulnerabilities (warning only)
|
||||
uses: aquasecurity/trivy-action@0.33.1
|
||||
with:
|
||||
image-ref: ${{ env.IMAGE_NAME }}:test
|
||||
severity: "MEDIUM,LOW"
|
||||
exit-code: "0"
|
||||
format: "table"
|
||||
cache-dir: .trivy-cache
|
||||
continue-on-error: true
|
||||
|
||||
- name: Generate SARIF report for GitHub Security
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: aquasecurity/trivy-action@0.33.1
|
||||
with:
|
||||
image-ref: ${{ env.IMAGE_NAME }}:test
|
||||
format: "sarif"
|
||||
output: "trivy-results.sarif"
|
||||
cache-dir: .trivy-cache
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload Trivy scan results
|
||||
|
||||
@@ -164,8 +164,10 @@ jobs:
|
||||
name: Publish to TestPyPI
|
||||
needs: [build, test-install]
|
||||
runs-on: ubuntu-latest
|
||||
# Only publish prereleases to TestPyPI
|
||||
if: contains(github.ref, '-')
|
||||
# Only publish alpha releases to TestPyPI for early testing.
|
||||
# Beta and RC releases go to PyPI since they are closer to stable.
|
||||
# This is intentional - do not change to contains(github.ref, '-').
|
||||
if: contains(github.ref, '-alpha')
|
||||
environment:
|
||||
name: testpypi
|
||||
url: https://test.pypi.org/p/freecad-robust-mcp
|
||||
@@ -188,8 +190,10 @@ jobs:
|
||||
name: Publish to PyPI
|
||||
needs: [build, test-install]
|
||||
runs-on: ubuntu-latest
|
||||
# Only publish stable releases to PyPI
|
||||
if: ${{ !contains(github.ref, '-') }}
|
||||
# Publish beta, rc, and stable releases to PyPI (not alpha).
|
||||
# Alpha releases are too experimental for PyPI - they go to TestPyPI only.
|
||||
# This is intentional - do not change to !contains(github.ref, '-').
|
||||
if: ${{ !contains(github.ref, '-alpha') }}
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/p/freecad-robust-mcp
|
||||
@@ -245,9 +249,10 @@ jobs:
|
||||
echo ""
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
if [[ "$REF_NAME" == *"-"* ]]; then
|
||||
# Alpha releases go to TestPyPI only; beta, rc, and stable go to PyPI
|
||||
if [[ "$REF_NAME" == *"-alpha"* ]]; then
|
||||
{
|
||||
echo "**Type:** Prerelease (published to TestPyPI)"
|
||||
echo "**Type:** Alpha prerelease (published to TestPyPI only)"
|
||||
echo ""
|
||||
echo "### Install from TestPyPI"
|
||||
echo ""
|
||||
@@ -255,6 +260,38 @@ jobs:
|
||||
echo "pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ freecad-robust-mcp"
|
||||
echo "\`\`\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
elif [[ "$REF_NAME" == *"-beta"* ]]; then
|
||||
{
|
||||
echo "**Type:** Beta prerelease (published to PyPI)"
|
||||
echo ""
|
||||
echo "### Install from PyPI"
|
||||
echo ""
|
||||
echo "\`\`\`bash"
|
||||
echo "pip install freecad-robust-mcp"
|
||||
echo "\`\`\`"
|
||||
echo ""
|
||||
echo "Or with uv:"
|
||||
echo ""
|
||||
echo "\`\`\`bash"
|
||||
echo "uv pip install freecad-robust-mcp"
|
||||
echo "\`\`\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
elif [[ "$REF_NAME" == *"-rc"* ]]; then
|
||||
{
|
||||
echo "**Type:** Release candidate (published to PyPI)"
|
||||
echo ""
|
||||
echo "### Install from PyPI"
|
||||
echo ""
|
||||
echo "\`\`\`bash"
|
||||
echo "pip install freecad-robust-mcp"
|
||||
echo "\`\`\`"
|
||||
echo ""
|
||||
echo "Or with uv:"
|
||||
echo ""
|
||||
echo "\`\`\`bash"
|
||||
echo "uv pip install freecad-robust-mcp"
|
||||
echo "\`\`\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
{
|
||||
echo "**Type:** Stable release (published to PyPI)"
|
||||
|
||||
@@ -97,6 +97,10 @@ cython_debug/
|
||||
# Ruff
|
||||
.ruff_cache/
|
||||
|
||||
# Trivy (pre-commit and CI cache)
|
||||
.pre-commit-trivy-cache/
|
||||
.trivy-cache/
|
||||
|
||||
# UV
|
||||
# Note: uv.lock is committed for reproducible CI builds
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ uv = "0.9" # uv package manager
|
||||
just = "1.43" # task runner
|
||||
pre-commit = "4.5" # pre-commit hooks
|
||||
github-cli = "2.74" # GitHub CLI for PR/issue management
|
||||
trivy = "0.62" # container vulnerability scanner
|
||||
|
||||
[env]
|
||||
# FreeCAD connection mode:
|
||||
|
||||
@@ -221,6 +221,21 @@ repos:
|
||||
- id: hadolint-docker
|
||||
name: hadolint (Dockerfile linter)
|
||||
|
||||
# ==========================================================================
|
||||
# Dockerfile Security Scanning (Misconfigurations)
|
||||
# ==========================================================================
|
||||
- repo: https://github.com/mxab/pre-commit-trivy.git
|
||||
rev: v0.16.0
|
||||
hooks:
|
||||
- id: trivyconfig-docker
|
||||
name: trivy (Dockerfile misconfig)
|
||||
args:
|
||||
- --severity
|
||||
- HIGH,CRITICAL
|
||||
- --exit-code
|
||||
- "1"
|
||||
- .
|
||||
|
||||
# ==========================================================================
|
||||
# Commit Message Linting
|
||||
# ==========================================================================
|
||||
@@ -240,4 +255,5 @@ ci:
|
||||
skip:
|
||||
- mypy # Needs dependencies installed
|
||||
- hadolint-docker # Needs Docker
|
||||
- trivyconfig-docker # Needs Docker
|
||||
- trufflehog # Can be slow in CI
|
||||
|
||||
+24
-16
@@ -3,6 +3,9 @@
|
||||
# FreeCAD MCP Server Dockerfile
|
||||
# Multi-stage build with BuildKit optimizations for multi-arch support
|
||||
#
|
||||
# Uses Alpine Linux for minimal image size and reduced CVE surface.
|
||||
# Alpine has significantly fewer vulnerabilities than Debian-based images.
|
||||
#
|
||||
# Build:
|
||||
# docker build -t freecad-mcp .
|
||||
#
|
||||
@@ -15,24 +18,22 @@
|
||||
# =============================================================================
|
||||
# Stage 1: Builder - Install dependencies and build the package
|
||||
# =============================================================================
|
||||
FROM python:3.11-slim AS builder
|
||||
FROM python:3.11-alpine AS builder
|
||||
|
||||
# Install build dependencies
|
||||
# hadolint ignore=DL3008
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
# Install build dependencies for compiling Python packages with native extensions
|
||||
# hadolint ignore=DL3018
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
libffi-dev
|
||||
|
||||
# Set up working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install uv for fast dependency management
|
||||
# Using pip cache mount for faster rebuilds
|
||||
# Upgrade pip to fix CVE-2025-8869, then install uv for fast dependency management
|
||||
# hadolint ignore=DL3013
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
pip install --no-compile uv
|
||||
pip install --no-cache-dir --upgrade "pip>=25.3" && \
|
||||
pip install --no-cache-dir --no-compile uv
|
||||
|
||||
# Copy only dependency files first for better layer caching
|
||||
COPY pyproject.toml README.md ./
|
||||
@@ -53,7 +54,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
# =============================================================================
|
||||
# Stage 2: Runtime - Minimal image for running the server
|
||||
# =============================================================================
|
||||
FROM python:3.11-slim AS runtime
|
||||
FROM python:3.11-alpine AS runtime
|
||||
|
||||
# Labels for container metadata (OCI Image Spec)
|
||||
# Note: version, revision, and created are set dynamically in CI/CD workflows
|
||||
@@ -65,11 +66,18 @@ LABEL org.opencontainers.image.title="FreeCAD MCP Server" \
|
||||
org.opencontainers.image.licenses="MIT" \
|
||||
org.opencontainers.image.vendor="Sean P. Kane" \
|
||||
org.opencontainers.image.authors="Sean P. Kane <spkane@gmail.com>" \
|
||||
org.opencontainers.image.base.name="python:3.11-slim"
|
||||
org.opencontainers.image.base.name="python:3.11-alpine"
|
||||
|
||||
# Create non-root user for security
|
||||
RUN groupadd --gid 1000 mcpuser && \
|
||||
useradd --uid 1000 --gid 1000 --shell /bin/bash --create-home mcpuser
|
||||
# Create non-root user for security (Alpine uses addgroup/adduser)
|
||||
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.
|
||||
# hadolint ignore=DL3013
|
||||
RUN pip install --no-cache-dir --upgrade "pip>=25.3"
|
||||
|
||||
# Copy virtual environment from builder
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# FreeCAD Tools and MCP Server
|
||||
|
||||
[](https://github.com/spkane/freecad-robust-mcp-and-more/actions/workflows/test.yaml)
|
||||
[](https://github.com/spkane/freecad-robust-mcp-and-more/actions/workflows/docker.yaml)
|
||||
[](https://github.com/spkane/freecad-robust-mcp-and-more/actions/workflows/pre-commit.yaml)
|
||||
[](https://github.com/spkane/freecad-robust-mcp-and-more/actions/workflows/codeql.yaml)
|
||||
|
||||
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that enables integration between AI assistants (Claude, GPT, and other MCP-compatible tools) and [FreeCAD](https://www.freecadweb.org/), allowing AI-assisted development and debugging of 3D models, macros, and workbenches.
|
||||
|
||||
> Also includes standalone FreeCAD macros for common tasks.
|
||||
@@ -13,6 +18,7 @@ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that
|
||||
- [Features](#features)
|
||||
- [Requirements](#requirements)
|
||||
- [For Users](#for-users)
|
||||
- [Quick Links](#quick-links)
|
||||
- [MCP Server](#mcp-server)
|
||||
- [Installation](#installation)
|
||||
- [Using pip (recommended)](#using-pip-recommended)
|
||||
@@ -86,6 +92,14 @@ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that
|
||||
|
||||
This section covers installation and usage for end users who want to use the MCP server with AI assistants or the standalone FreeCAD macros.
|
||||
|
||||
### Quick Links
|
||||
|
||||
| Resource | Description |
|
||||
| --------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| [Docker Hub](https://hub.docker.com/r/spkane/freecad-robust-mcp) | Pre-built Docker images for easy deployment |
|
||||
| [PyPI](https://pypi.org/project/freecad-robust-mcp/) | Python package for pip installation |
|
||||
| [GitHub Releases](https://github.com/spkane/freecad-robust-mcp-and-more/releases) | Release archives, changelogs, and standalone macro downloads |
|
||||
|
||||
## MCP Server
|
||||
|
||||
> **Note**: Since this repository has more than just the MCP server in it, the Linux container and PyPi projects releases are both simply named `freecad-robust-mcp` which differs from the name of this git repository.
|
||||
|
||||
+33
-3
@@ -1,8 +1,8 @@
|
||||
# Docker build and run commands
|
||||
# Usage: just docker::build, just docker::build-all, etc.
|
||||
|
||||
# Default Docker image name
|
||||
image_name := "freecad-mcp"
|
||||
# Default Docker image name (matches Docker Hub and PyPI package name)
|
||||
image_name := "freecad-robust-mcp"
|
||||
registry := "spkane"
|
||||
|
||||
# Project root directory (justfile_directory() returns the main justfile's directory)
|
||||
@@ -53,7 +53,7 @@ shell:
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
-e FREECAD_MODE=xmlrpc \
|
||||
-e FREECAD_SOCKET_HOST=host.docker.internal \
|
||||
--entrypoint /bin/bash \
|
||||
--entrypoint /bin/sh \
|
||||
{{image_name}}
|
||||
|
||||
# Show image size and layers
|
||||
@@ -67,6 +67,36 @@ clean:
|
||||
docker rmi {{image_name}} 2>/dev/null || true
|
||||
docker rmi {{registry}}/{{image_name}} 2>/dev/null || true
|
||||
|
||||
# Scan Docker image for vulnerabilities (warn on all severities)
|
||||
scan:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
echo "Scanning {{image_name}} for vulnerabilities..."
|
||||
echo ""
|
||||
trivy image --severity HIGH,CRITICAL {{image_name}}
|
||||
echo ""
|
||||
echo "Scanning for MEDIUM/LOW (informational)..."
|
||||
trivy image --severity MEDIUM,LOW {{image_name}} || true
|
||||
|
||||
# Scan Docker image with strict settings (fail on HIGH or CRITICAL)
|
||||
scan-strict:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
echo "Scanning {{image_name}} for HIGH/CRITICAL vulnerabilities..."
|
||||
echo "(Build will fail if any HIGH or CRITICAL CVEs are found)"
|
||||
echo ""
|
||||
trivy image --severity HIGH,CRITICAL --exit-code 1 {{image_name}}
|
||||
echo ""
|
||||
echo "✓ No HIGH or CRITICAL vulnerabilities found!"
|
||||
echo ""
|
||||
echo "Scanning for MEDIUM/LOW (informational only)..."
|
||||
trivy image --severity MEDIUM,LOW --exit-code 0 {{image_name}} || true
|
||||
|
||||
# Scan Docker image and output SARIF report
|
||||
scan-sarif output="trivy-results.sarif":
|
||||
trivy image --format sarif --output {{output}} {{image_name}}
|
||||
@echo "SARIF report written to {{output}}"
|
||||
|
||||
# Create and configure buildx builder for multi-arch builds
|
||||
setup-buildx:
|
||||
#!/usr/bin/env bash
|
||||
|
||||
Reference in New Issue
Block a user