Add comprehensive release infrastructure and tooling
- Add automated GitHub Actions workflow for multi-platform releases - Add release script with version management and validation - Add Docker container support with multi-stage builds - Add comprehensive release documentation and templates - Update Cargo.toml with complete package metadata - Add command-line argument parsing with security options - Update README with detailed configuration examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(chmod:*)"
|
"Bash(chmod:*)",
|
||||||
|
"Bash(cargo build:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
name: Release Checklist
|
||||||
|
about: Checklist for preparing a new release
|
||||||
|
title: 'Release v[VERSION]'
|
||||||
|
labels: 'release'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-release Checklist
|
||||||
|
|
||||||
|
- [ ] All planned features and fixes are merged
|
||||||
|
- [ ] All tests are passing on main branch
|
||||||
|
- [ ] Documentation is updated
|
||||||
|
- [ ] CHANGELOG.md is updated (if maintained separately)
|
||||||
|
- [ ] Version is updated in Cargo.toml
|
||||||
|
- [ ] No critical security vulnerabilities in dependencies
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
- [ ] Run `./scripts/release.sh [patch|minor|major|version X.Y.Z]`
|
||||||
|
- [ ] Verify all CI checks pass
|
||||||
|
- [ ] Tag is created and pushed
|
||||||
|
- [ ] GitHub release is created automatically
|
||||||
|
- [ ] Binaries are built for all platforms
|
||||||
|
- [ ] Crate is published to crates.io (for stable releases)
|
||||||
|
- [ ] Docker images are pushed
|
||||||
|
|
||||||
|
## Post-release Tasks
|
||||||
|
|
||||||
|
- [ ] Verify release artifacts are available
|
||||||
|
- [ ] Test installation from released binaries
|
||||||
|
- [ ] Update any dependent projects
|
||||||
|
- [ ] Announce release (if applicable)
|
||||||
|
|
||||||
|
## Release Notes
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Add release notes here:
|
||||||
|
- New features
|
||||||
|
- Bug fixes
|
||||||
|
- Breaking changes
|
||||||
|
- Performance improvements
|
||||||
|
- Security fixes
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Verification Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test the release script (dry run)
|
||||||
|
./scripts/release.sh patch --dry-run
|
||||||
|
|
||||||
|
# Run pre-release checks
|
||||||
|
./scripts/release.sh check
|
||||||
|
|
||||||
|
# Create the actual release
|
||||||
|
./scripts/release.sh patch # or minor/major/version X.Y.Z
|
||||||
|
```
|
||||||
@@ -0,0 +1,490 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version to release (e.g., v1.0.0)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-release:
|
||||||
|
name: Validate Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.get-version.outputs.version }}
|
||||||
|
tag: ${{ steps.get-version.outputs.tag }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get version from tag or input
|
||||||
|
id: get-version
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||||
|
VERSION="${{ github.event.inputs.version }}"
|
||||||
|
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||||
|
echo "tag=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
VERSION="${GITHUB_REF#refs/tags/}"
|
||||||
|
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||||
|
echo "tag=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Validate version format
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.get-version.outputs.version }}"
|
||||||
|
if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
||||||
|
echo "❌ Invalid version format: $VERSION"
|
||||||
|
echo "Expected format: X.Y.Z or X.Y.Z-suffix"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Valid version format: $VERSION"
|
||||||
|
|
||||||
|
- name: Check if version matches Cargo.toml
|
||||||
|
run: |
|
||||||
|
CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
|
||||||
|
INPUT_VERSION="${{ steps.get-version.outputs.version }}"
|
||||||
|
if [ "$CARGO_VERSION" != "$INPUT_VERSION" ]; then
|
||||||
|
echo "❌ Version mismatch:"
|
||||||
|
echo " Cargo.toml: $CARGO_VERSION"
|
||||||
|
echo " Tag/Input: $INPUT_VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Version matches Cargo.toml: $INPUT_VERSION"
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Run Tests
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
needs: validate-release
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
rust: [stable]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
|
||||||
|
- name: Cache Cargo registry
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Install system dependencies (Ubuntu)
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
libfontconfig1-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libpng-dev
|
||||||
|
|
||||||
|
- name: Install system dependencies (macOS)
|
||||||
|
if: matrix.os == 'macos-latest'
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew install pkg-config freetype jpeg libpng
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --all-features --verbose
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build Release Artifacts
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
needs: [validate-release, test]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, use-cross: false }
|
||||||
|
- { os: ubuntu-latest, target: x86_64-unknown-linux-musl, use-cross: true }
|
||||||
|
- { os: ubuntu-latest, target: aarch64-unknown-linux-gnu, use-cross: true }
|
||||||
|
- { os: ubuntu-latest, target: aarch64-unknown-linux-musl, use-cross: true }
|
||||||
|
- { os: windows-latest, target: x86_64-pc-windows-msvc, use-cross: false }
|
||||||
|
- { os: macos-latest, target: x86_64-apple-darwin, use-cross: false }
|
||||||
|
- { os: macos-latest, target: aarch64-apple-darwin, use-cross: false }
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.job.target }}
|
||||||
|
|
||||||
|
- name: Install cross compilation tools
|
||||||
|
if: matrix.job.use-cross
|
||||||
|
run: |
|
||||||
|
cargo install cross --git https://github.com/cross-rs/cross
|
||||||
|
|
||||||
|
- name: Cache Cargo registry
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-${{ matrix.job.target }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Install system dependencies (Ubuntu)
|
||||||
|
if: matrix.job.os == 'ubuntu-latest' && !matrix.job.use-cross
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
libfontconfig1-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libpng-dev
|
||||||
|
|
||||||
|
- name: Install system dependencies (macOS)
|
||||||
|
if: matrix.job.os == 'macos-latest'
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew install pkg-config freetype jpeg libpng
|
||||||
|
|
||||||
|
- name: Build release binary
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.job.use-cross }}" = "true" ]; then
|
||||||
|
cross build --release --target ${{ matrix.job.target }} --all-features
|
||||||
|
else
|
||||||
|
cargo build --release --target ${{ matrix.job.target }} --all-features
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Prepare release archive
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
VERSION="${{ needs.validate-release.outputs.version }}"
|
||||||
|
TARGET="${{ matrix.job.target }}"
|
||||||
|
|
||||||
|
# Create staging directory
|
||||||
|
mkdir -p staging
|
||||||
|
|
||||||
|
# Copy binary
|
||||||
|
if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
|
||||||
|
cp "target/${TARGET}/release/docx-mcp.exe" staging/
|
||||||
|
BINARY="docx-mcp.exe"
|
||||||
|
else
|
||||||
|
cp "target/${TARGET}/release/docx-mcp" staging/
|
||||||
|
BINARY="docx-mcp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy additional files
|
||||||
|
cp README.md staging/
|
||||||
|
cp LICENSE staging/
|
||||||
|
|
||||||
|
# Create archive name
|
||||||
|
ARCHIVE="docx-mcp-${VERSION}-${TARGET}"
|
||||||
|
|
||||||
|
# Create archive
|
||||||
|
cd staging
|
||||||
|
if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
|
||||||
|
7z a "../${ARCHIVE}.zip" *
|
||||||
|
echo "ARCHIVE=${ARCHIVE}.zip" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
tar czf "../${ARCHIVE}.tar.gz" *
|
||||||
|
echo "ARCHIVE=${ARCHIVE}.tar.gz" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Generate checksums
|
||||||
|
if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
|
||||||
|
certutil -hashfile "${ARCHIVE}.zip" SHA256 > "${ARCHIVE}.zip.sha256"
|
||||||
|
else
|
||||||
|
shasum -a 256 "${ARCHIVE}.tar.gz" > "${ARCHIVE}.tar.gz.sha256"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "BINARY=${BINARY}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Upload release artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-${{ matrix.job.target }}
|
||||||
|
path: |
|
||||||
|
${{ env.ARCHIVE }}
|
||||||
|
${{ env.ARCHIVE }}.sha256
|
||||||
|
|
||||||
|
create-release:
|
||||||
|
name: Create GitHub Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [validate-release, build]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
|
||||||
|
- name: Prepare release assets
|
||||||
|
run: |
|
||||||
|
mkdir -p release-assets
|
||||||
|
find artifacts -type f -name "*.tar.gz" -o -name "*.zip" -o -name "*.sha256" | \
|
||||||
|
xargs -I {} cp {} release-assets/
|
||||||
|
ls -la release-assets/
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
VERSION="${{ needs.validate-release.outputs.version }}"
|
||||||
|
TAG="${{ needs.validate-release.outputs.tag }}"
|
||||||
|
|
||||||
|
# Get previous tag
|
||||||
|
PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${TAG}$" | head -1)
|
||||||
|
|
||||||
|
echo "Generating changelog from ${PREV_TAG} to ${TAG}"
|
||||||
|
|
||||||
|
# Generate changelog
|
||||||
|
if [ -n "$PREV_TAG" ]; then
|
||||||
|
CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges ${PREV_TAG}..HEAD)
|
||||||
|
else
|
||||||
|
CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create release notes
|
||||||
|
cat > release-notes.md << EOF
|
||||||
|
## What's Changed
|
||||||
|
|
||||||
|
${CHANGELOG}
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Pre-built Binaries
|
||||||
|
|
||||||
|
Download the appropriate binary for your system:
|
||||||
|
|
||||||
|
- **Linux x86_64**: \`docx-mcp-${VERSION}-x86_64-unknown-linux-gnu.tar.gz\`
|
||||||
|
- **Linux x86_64 (musl)**: \`docx-mcp-${VERSION}-x86_64-unknown-linux-musl.tar.gz\`
|
||||||
|
- **Linux ARM64**: \`docx-mcp-${VERSION}-aarch64-unknown-linux-gnu.tar.gz\`
|
||||||
|
- **macOS Intel**: \`docx-mcp-${VERSION}-x86_64-apple-darwin.tar.gz\`
|
||||||
|
- **macOS Apple Silicon**: \`docx-mcp-${VERSION}-aarch64-apple-darwin.tar.gz\`
|
||||||
|
- **Windows x86_64**: \`docx-mcp-${VERSION}-x86_64-pc-windows-msvc.zip\`
|
||||||
|
|
||||||
|
### From Source
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
cargo install --git https://github.com/hongkongkiwi/docx-mcp --tag ${TAG}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
All binaries are provided with SHA256 checksums for verification:
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
# Linux/macOS
|
||||||
|
shasum -a 256 -c docx-mcp-${VERSION}-your-target.tar.gz.sha256
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
certutil -hashfile docx-mcp-${VERSION}-x86_64-pc-windows-msvc.zip SHA256
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Full Changelog
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/hongkongkiwi/docx-mcp/compare/${PREV_TAG}...${TAG}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "CHANGELOG_FILE=release-notes.md" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.validate-release.outputs.tag }}
|
||||||
|
name: Release ${{ needs.validate-release.outputs.tag }}
|
||||||
|
body_path: ${{ steps.changelog.outputs.CHANGELOG_FILE }}
|
||||||
|
files: release-assets/*
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(needs.validate-release.outputs.version, '-') }}
|
||||||
|
generate_release_notes: false
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
publish-crate:
|
||||||
|
name: Publish to crates.io
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [validate-release, create-release]
|
||||||
|
if: ${{ !contains(needs.validate-release.outputs.version, '-') }} # Only publish stable releases
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
libfontconfig1-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libpng-dev
|
||||||
|
|
||||||
|
- name: Cache Cargo registry
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ubuntu-cargo-publish-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Verify package
|
||||||
|
run: cargo package --dry-run
|
||||||
|
|
||||||
|
- name: Publish to crates.io
|
||||||
|
run: cargo publish
|
||||||
|
env:
|
||||||
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
docker-release:
|
||||||
|
name: Build and Push Docker Images
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [validate-release, create-release]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
ghcr.io/${{ github.repository }}
|
||||||
|
${{ secrets.DOCKERHUB_USERNAME && format('{0}/docx-mcp', secrets.DOCKERHUB_USERNAME) || '' }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=tag
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
update-docs:
|
||||||
|
name: Update Documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [validate-release, create-release]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
libfontconfig1-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libpng-dev
|
||||||
|
|
||||||
|
- name: Generate documentation
|
||||||
|
run: |
|
||||||
|
cargo doc --all-features --no-deps
|
||||||
|
echo '<meta http-equiv="refresh" content="0; url=docx_mcp">' > target/doc/index.html
|
||||||
|
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./target/doc
|
||||||
|
cname: docs.example.com # Replace with your custom domain if you have one
|
||||||
|
|
||||||
|
notify-success:
|
||||||
|
name: Notify Release Success
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [create-release, publish-crate, docker-release, update-docs]
|
||||||
|
if: success()
|
||||||
|
steps:
|
||||||
|
- name: Success notification
|
||||||
|
run: |
|
||||||
|
echo "🎉 Release ${{ needs.validate-release.outputs.tag }} completed successfully!"
|
||||||
|
echo "- ✅ GitHub release created"
|
||||||
|
echo "- ✅ Binaries built for all platforms"
|
||||||
|
echo "- ✅ Published to crates.io"
|
||||||
|
echo "- ✅ Docker images pushed"
|
||||||
|
echo "- ✅ Documentation updated"
|
||||||
|
|
||||||
|
notify-failure:
|
||||||
|
name: Notify Release Failure
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [validate-release, test, build, create-release, publish-crate, docker-release, update-docs]
|
||||||
|
if: failure()
|
||||||
|
steps:
|
||||||
|
- name: Failure notification
|
||||||
|
run: |
|
||||||
|
echo "❌ Release ${{ needs.validate-release.outputs.tag }} failed!"
|
||||||
|
echo "Please check the workflow logs for details."
|
||||||
|
exit 1
|
||||||
+23
-3
@@ -2,11 +2,28 @@
|
|||||||
name = "docx-mcp"
|
name = "docx-mcp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = ["Your Name <your.email@example.com>"]
|
||||||
|
description = "A comprehensive Model Context Protocol (MCP) server for Microsoft Word DOCX file manipulation"
|
||||||
|
documentation = "https://docs.rs/docx-mcp"
|
||||||
|
homepage = "https://github.com/hongkongkiwi/docx-mcp"
|
||||||
|
repository = "https://github.com/hongkongkiwi/docx-mcp"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT"
|
||||||
|
keywords = ["mcp", "docx", "word", "document", "pdf"]
|
||||||
|
categories = ["text-processing", "api-bindings", "command-line-utilities"]
|
||||||
|
exclude = [
|
||||||
|
"/.github/*",
|
||||||
|
"/tests/fixtures/*",
|
||||||
|
"/example/*",
|
||||||
|
"/benches/*",
|
||||||
|
"/.gitignore",
|
||||||
|
"/deny.toml"
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Official MCP SDK
|
# Official MCP SDK
|
||||||
mcp-server = "0.3"
|
mcp-server = "0.1"
|
||||||
mcp-core = "0.3"
|
mcp-core = "0.1"
|
||||||
|
|
||||||
# Async runtime
|
# Async runtime
|
||||||
tokio = { version = "1.40", features = ["full"] }
|
tokio = { version = "1.40", features = ["full"] }
|
||||||
@@ -26,7 +43,7 @@ lopdf = "0.34"
|
|||||||
rusttype = "0.9" # Font rendering in pure Rust
|
rusttype = "0.9" # Font rendering in pure Rust
|
||||||
|
|
||||||
# Embedded fonts for PDF
|
# Embedded fonts for PDF
|
||||||
include_bytes_plus = "1.0"
|
include-bytes-plus = "1.0"
|
||||||
|
|
||||||
# Image processing (pure Rust)
|
# Image processing (pure Rust)
|
||||||
image = { version = "0.25", features = ["png", "jpeg", "webp", "bmp", "gif"] }
|
image = { version = "0.25", features = ["png", "jpeg", "webp", "bmp", "gif"] }
|
||||||
@@ -66,6 +83,9 @@ chrono = { version = "0.4", features = ["serde"] }
|
|||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
once_cell = "1.20"
|
once_cell = "1.20"
|
||||||
|
|
||||||
|
# Command line argument parsing
|
||||||
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
|
|
||||||
# Optional external tool support
|
# Optional external tool support
|
||||||
headless_chrome = { version = "1.0", optional = true }
|
headless_chrome = { version = "1.0", optional = true }
|
||||||
wkhtmltopdf = { version = "0.4", optional = true }
|
wkhtmltopdf = { version = "0.4", optional = true }
|
||||||
|
|||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
# Multi-stage Docker build for docx-mcp
|
||||||
|
FROM rust:1.75-slim as builder
|
||||||
|
|
||||||
|
# Install system dependencies for building
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
libfontconfig1-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libpng-dev \
|
||||||
|
build-essential \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy manifests
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY build.rs ./
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY src/ ./src/
|
||||||
|
COPY benches/ ./benches/
|
||||||
|
COPY tests/ ./tests/
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN cargo build --release --all-features
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
# Install runtime dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libssl3 \
|
||||||
|
libfontconfig1 \
|
||||||
|
libfreetype6 \
|
||||||
|
libjpeg62-turbo \
|
||||||
|
libpng16-16 \
|
||||||
|
ca-certificates \
|
||||||
|
libreoffice \
|
||||||
|
poppler-utils \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN groupadd -r docxmcp && useradd -r -g docxmcp -s /bin/bash -d /app docxmcp
|
||||||
|
|
||||||
|
# Create app directory and set ownership
|
||||||
|
WORKDIR /app
|
||||||
|
RUN chown -R docxmcp:docxmcp /app
|
||||||
|
|
||||||
|
# Copy the built binary from builder stage
|
||||||
|
COPY --from=builder /app/target/release/docx-mcp /usr/local/bin/docx-mcp
|
||||||
|
RUN chmod +x /usr/local/bin/docx-mcp
|
||||||
|
|
||||||
|
# Copy additional files if needed
|
||||||
|
COPY README.md LICENSE ./
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER docxmcp
|
||||||
|
|
||||||
|
# Create temp directory for document processing
|
||||||
|
RUN mkdir -p /tmp/docx-mcp && chmod 755 /tmp/docx-mcp
|
||||||
|
|
||||||
|
# Expose default MCP port (though MCP typically uses stdin/stdout)
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD /usr/local/bin/docx-mcp --version || exit 1
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
ENV DOCX_MCP_TEMP_DIR=/tmp/docx-mcp
|
||||||
|
|
||||||
|
# Default command
|
||||||
|
CMD ["/usr/local/bin/docx-mcp"]
|
||||||
@@ -60,8 +60,13 @@ The server includes comprehensive security features for enterprise and restricte
|
|||||||
### Readonly Mode
|
### Readonly Mode
|
||||||
```bash
|
```bash
|
||||||
# Enable readonly mode - only allows document viewing and analysis
|
# Enable readonly mode - only allows document viewing and analysis
|
||||||
|
|
||||||
|
# Using environment variables
|
||||||
export DOCX_MCP_READONLY=true
|
export DOCX_MCP_READONLY=true
|
||||||
./target/release/docx-mcp
|
./target/release/docx-mcp
|
||||||
|
|
||||||
|
# Using command line arguments
|
||||||
|
./target/release/docx-mcp --readonly
|
||||||
```
|
```
|
||||||
|
|
||||||
In readonly mode, only these operations are allowed:
|
In readonly mode, only these operations are allowed:
|
||||||
@@ -74,32 +79,51 @@ In readonly mode, only these operations are allowed:
|
|||||||
### Command Filtering
|
### Command Filtering
|
||||||
```bash
|
```bash
|
||||||
# Whitelist specific commands only
|
# Whitelist specific commands only
|
||||||
|
|
||||||
|
# Using environment variables
|
||||||
export DOCX_MCP_WHITELIST="open_document,extract_text,get_metadata,export_to_markdown"
|
export DOCX_MCP_WHITELIST="open_document,extract_text,get_metadata,export_to_markdown"
|
||||||
|
|
||||||
|
# Using command line arguments
|
||||||
|
./target/release/docx-mcp --whitelist open_document,extract_text,get_metadata,export_to_markdown
|
||||||
|
|
||||||
# Or blacklist dangerous commands
|
# Or blacklist dangerous commands
|
||||||
|
|
||||||
|
# Using environment variables
|
||||||
export DOCX_MCP_BLACKLIST="save_document,convert_to_pdf,merge_documents"
|
export DOCX_MCP_BLACKLIST="save_document,convert_to_pdf,merge_documents"
|
||||||
|
|
||||||
|
# Using command line arguments
|
||||||
|
./target/release/docx-mcp --blacklist save_document,convert_to_pdf,merge_documents
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sandbox Mode
|
### Sandbox Mode
|
||||||
```bash
|
```bash
|
||||||
# Restrict all file operations to temp directory only
|
# Restrict all file operations to temp directory only
|
||||||
|
|
||||||
|
# Using environment variables
|
||||||
export DOCX_MCP_SANDBOX=true
|
export DOCX_MCP_SANDBOX=true
|
||||||
./target/release/docx-mcp
|
./target/release/docx-mcp
|
||||||
|
|
||||||
|
# Using command line arguments
|
||||||
|
./target/release/docx-mcp --sandbox
|
||||||
```
|
```
|
||||||
|
|
||||||
### Resource Limits
|
### Resource Limits
|
||||||
```bash
|
```bash
|
||||||
# Set maximum document size (100MB default)
|
# Set maximum document size (100MB default)
|
||||||
|
|
||||||
|
# Using environment variables
|
||||||
export DOCX_MCP_MAX_SIZE=52428800 # 50MB
|
export DOCX_MCP_MAX_SIZE=52428800 # 50MB
|
||||||
|
|
||||||
# Set maximum number of open documents
|
|
||||||
export DOCX_MCP_MAX_DOCS=20
|
export DOCX_MCP_MAX_DOCS=20
|
||||||
|
|
||||||
# Disable external tools
|
|
||||||
export DOCX_MCP_NO_EXTERNAL_TOOLS=true
|
export DOCX_MCP_NO_EXTERNAL_TOOLS=true
|
||||||
|
|
||||||
# Disable network operations
|
|
||||||
export DOCX_MCP_NO_NETWORK=true
|
export DOCX_MCP_NO_NETWORK=true
|
||||||
|
./target/release/docx-mcp
|
||||||
|
|
||||||
|
# Using command line arguments
|
||||||
|
./target/release/docx-mcp \
|
||||||
|
--max-size 52428800 \
|
||||||
|
--max-docs 20 \
|
||||||
|
--no-external-tools \
|
||||||
|
--no-network
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🤖 AI Tool Integration
|
## 🤖 AI Tool Integration
|
||||||
@@ -125,6 +149,39 @@ Add to your Claude Desktop configuration file:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**With Security Options (using command-line arguments):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"docx": {
|
||||||
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
|
"args": ["--readonly", "--max-size", "52428800", "--no-network"],
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Security Options (using environment variables):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"docx": {
|
||||||
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
|
"args": [],
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info",
|
||||||
|
"DOCX_MCP_READONLY": "true",
|
||||||
|
"DOCX_MCP_MAX_SIZE": "52428800",
|
||||||
|
"DOCX_MCP_NO_NETWORK": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
After adding, restart Claude Desktop. You can then ask Claude to:
|
After adding, restart Claude Desktop. You can then ask Claude to:
|
||||||
- "Create a new Word document with our Q4 report"
|
- "Create a new Word document with our Q4 report"
|
||||||
- "Convert this DOCX file to PDF"
|
- "Convert this DOCX file to PDF"
|
||||||
@@ -135,6 +192,7 @@ After adding, restart Claude Desktop. You can then ask Claude to:
|
|||||||
|
|
||||||
Add to your Cursor settings (`~/.cursor/config.json` or through Settings UI):
|
Add to your Cursor settings (`~/.cursor/config.json` or through Settings UI):
|
||||||
|
|
||||||
|
**Basic Configuration:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcp": {
|
"mcp": {
|
||||||
@@ -151,10 +209,24 @@ Add to your Cursor settings (`~/.cursor/config.json` or through Settings UI):
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Windsurf (Codeium)
|
**With Security Options (using command-line arguments):**
|
||||||
|
```json
|
||||||
Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
{
|
||||||
|
"mcp": {
|
||||||
|
"servers": {
|
||||||
|
"docx": {
|
||||||
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
|
"args": ["--sandbox", "--whitelist", "open_document,extract_text,export_to_markdown"],
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Security Options (using environment variables):**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcp": {
|
"mcp": {
|
||||||
@@ -162,6 +234,46 @@ Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
|||||||
"docx": {
|
"docx": {
|
||||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
"args": [],
|
"args": [],
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info",
|
||||||
|
"DOCX_MCP_SANDBOX": "true",
|
||||||
|
"DOCX_MCP_WHITELIST": "open_document,extract_text,export_to_markdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windsurf (Codeium)
|
||||||
|
|
||||||
|
Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
||||||
|
|
||||||
|
**Basic Configuration:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcp": {
|
||||||
|
"servers": {
|
||||||
|
"docx": {
|
||||||
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
|
"args": [],
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Security Options (using arguments):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcp": {
|
||||||
|
"servers": {
|
||||||
|
"docx": {
|
||||||
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
|
"args": ["--readonly", "--no-external-tools"],
|
||||||
"env": {
|
"env": {
|
||||||
"RUST_LOG": "info"
|
"RUST_LOG": "info"
|
||||||
}
|
}
|
||||||
@@ -175,6 +287,7 @@ Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
|||||||
|
|
||||||
Add to your Continue configuration (`~/.continue/config.json`):
|
Add to your Continue configuration (`~/.continue/config.json`):
|
||||||
|
|
||||||
|
**Basic Configuration:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
@@ -192,10 +305,29 @@ Add to your Continue configuration (`~/.continue/config.json`):
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**With Security Options:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"title": "Your Model",
|
||||||
|
"provider": "your-provider",
|
||||||
|
"mcp_servers": {
|
||||||
|
"docx": {
|
||||||
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
|
"args": ["--sandbox", "--max-size", "10485760"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### VS Code with MCP Extension
|
### VS Code with MCP Extension
|
||||||
|
|
||||||
If using the MCP extension for VS Code, add to your workspace settings (`.vscode/settings.json`):
|
If using the MCP extension for VS Code, add to your workspace settings (`.vscode/settings.json`):
|
||||||
|
|
||||||
|
**Basic Configuration:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcp.servers": {
|
"mcp.servers": {
|
||||||
@@ -210,6 +342,67 @@ If using the MCP extension for VS Code, add to your workspace settings (`.vscode
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**With Security Options:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcp.servers": {
|
||||||
|
"docx": {
|
||||||
|
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||||
|
"args": ["--readonly", "--blacklist", "save_document,merge_documents"],
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Command Line Arguments
|
||||||
|
|
||||||
|
The DOCX MCP server supports the following command-line arguments for configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docx-mcp --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Arguments
|
||||||
|
|
||||||
|
| Argument | Environment Variable | Description | Example |
|
||||||
|
|----------|---------------------|-------------|---------|
|
||||||
|
| `--readonly` | `DOCX_MCP_READONLY=true` | Enable readonly mode - only viewing operations | `--readonly` |
|
||||||
|
| `--whitelist <COMMANDS>` | `DOCX_MCP_WHITELIST` | Comma-separated list of allowed commands | `--whitelist open_document,extract_text` |
|
||||||
|
| `--blacklist <COMMANDS>` | `DOCX_MCP_BLACKLIST` | Comma-separated list of forbidden commands | `--blacklist save_document,convert_to_pdf` |
|
||||||
|
| `--sandbox` | `DOCX_MCP_SANDBOX=true` | Restrict file operations to temp directory only | `--sandbox` |
|
||||||
|
| `--no-external-tools` | `DOCX_MCP_NO_EXTERNAL_TOOLS=true` | Disable external tools (LibreOffice, etc.) | `--no-external-tools` |
|
||||||
|
| `--no-network` | `DOCX_MCP_NO_NETWORK=true` | Disable network operations | `--no-network` |
|
||||||
|
| `--max-size <BYTES>` | `DOCX_MCP_MAX_SIZE` | Maximum document size in bytes | `--max-size 52428800` |
|
||||||
|
| `--max-docs <COUNT>` | `DOCX_MCP_MAX_DOCS` | Maximum number of open documents | `--max-docs 20` |
|
||||||
|
| `--help` | - | Show help information | `--help` |
|
||||||
|
| `--version` | - | Show version information | `--version` |
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic usage
|
||||||
|
./target/release/docx-mcp
|
||||||
|
|
||||||
|
# Readonly mode with size limit
|
||||||
|
./target/release/docx-mcp --readonly --max-size 10485760
|
||||||
|
|
||||||
|
# Sandbox mode with command whitelist
|
||||||
|
./target/release/docx-mcp --sandbox --whitelist open_document,extract_text,export_to_markdown
|
||||||
|
|
||||||
|
# Multiple security options
|
||||||
|
./target/release/docx-mcp \
|
||||||
|
--readonly \
|
||||||
|
--no-external-tools \
|
||||||
|
--no-network \
|
||||||
|
--max-size 52428800 \
|
||||||
|
--max-docs 10
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Command-line arguments take precedence over environment variables when both are specified.
|
||||||
|
|
||||||
## 📚 Features
|
## 📚 Features
|
||||||
|
|
||||||
### Document Operations
|
### Document Operations
|
||||||
|
|||||||
+249
@@ -0,0 +1,249 @@
|
|||||||
|
# Release Guide
|
||||||
|
|
||||||
|
This document describes the release process for docx-mcp.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The release process is automated using GitHub Actions and includes:
|
||||||
|
|
||||||
|
- Automated testing on multiple platforms
|
||||||
|
- Building release binaries for all supported targets
|
||||||
|
- Publishing to crates.io
|
||||||
|
- Creating GitHub releases with binaries
|
||||||
|
- Building and pushing Docker images
|
||||||
|
- Updating documentation
|
||||||
|
|
||||||
|
## Release Types
|
||||||
|
|
||||||
|
### Semantic Versioning
|
||||||
|
|
||||||
|
We follow [Semantic Versioning](https://semver.org/):
|
||||||
|
|
||||||
|
- **MAJOR**: Incompatible API changes
|
||||||
|
- **MINOR**: New features (backwards compatible)
|
||||||
|
- **PATCH**: Bug fixes (backwards compatible)
|
||||||
|
|
||||||
|
### Pre-release Versions
|
||||||
|
|
||||||
|
Pre-release versions can include suffixes like:
|
||||||
|
- `1.0.0-alpha.1` - Alpha releases
|
||||||
|
- `1.0.0-beta.1` - Beta releases
|
||||||
|
- `1.0.0-rc.1` - Release candidates
|
||||||
|
|
||||||
|
## Quick Release Process
|
||||||
|
|
||||||
|
For most releases, use the automated release script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Patch release (1.0.0 -> 1.0.1)
|
||||||
|
./scripts/release.sh patch
|
||||||
|
|
||||||
|
# Minor release (1.0.0 -> 1.1.0)
|
||||||
|
./scripts/release.sh minor
|
||||||
|
|
||||||
|
# Major release (1.0.0 -> 2.0.0)
|
||||||
|
./scripts/release.sh major
|
||||||
|
|
||||||
|
# Specific version
|
||||||
|
./scripts/release.sh version 1.5.0
|
||||||
|
|
||||||
|
# Pre-release
|
||||||
|
./scripts/release.sh version 1.0.0-beta.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Release Process
|
||||||
|
|
||||||
|
If you need to create a release manually:
|
||||||
|
|
||||||
|
### 1. Pre-release Checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all checks
|
||||||
|
./scripts/release.sh check
|
||||||
|
|
||||||
|
# Or manually:
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
cargo test --all-features
|
||||||
|
cargo build --release --all-features
|
||||||
|
cargo package --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Update Version
|
||||||
|
|
||||||
|
Update the version in `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
version = "1.2.3"
|
||||||
|
```
|
||||||
|
|
||||||
|
Update `Cargo.lock`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo update -p docx-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Commit and Tag
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add Cargo.toml Cargo.lock
|
||||||
|
git commit -m "Release v1.2.3"
|
||||||
|
git tag -a "v1.2.3" -m "Release v1.2.3"
|
||||||
|
git push origin main
|
||||||
|
git push origin v1.2.3
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. GitHub Actions
|
||||||
|
|
||||||
|
The release workflow will automatically:
|
||||||
|
|
||||||
|
1. Validate the release
|
||||||
|
2. Run tests on all platforms
|
||||||
|
3. Build binaries for all targets
|
||||||
|
4. Create GitHub release
|
||||||
|
5. Publish to crates.io (stable releases only)
|
||||||
|
6. Build and push Docker images
|
||||||
|
7. Update documentation
|
||||||
|
|
||||||
|
## Supported Platforms
|
||||||
|
|
||||||
|
Release binaries are built for:
|
||||||
|
|
||||||
|
- **Linux**: x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl
|
||||||
|
- **Linux ARM**: aarch64-unknown-linux-gnu, aarch64-unknown-linux-musl
|
||||||
|
- **macOS**: x86_64-apple-darwin, aarch64-apple-darwin
|
||||||
|
- **Windows**: x86_64-pc-windows-msvc
|
||||||
|
|
||||||
|
## Docker Images
|
||||||
|
|
||||||
|
Docker images are published to:
|
||||||
|
|
||||||
|
- GitHub Container Registry: `ghcr.io/hongkongkiwi/docx-mcp`
|
||||||
|
- Docker Hub: `dockerhub-username/docx-mcp` (if configured)
|
||||||
|
|
||||||
|
Tags include:
|
||||||
|
- `latest` - Latest stable release
|
||||||
|
- `v1.2.3` - Specific version
|
||||||
|
- `1.2.3` - Semantic version
|
||||||
|
- `1.2` - Major.minor version
|
||||||
|
- `1` - Major version
|
||||||
|
|
||||||
|
## Publishing to crates.io
|
||||||
|
|
||||||
|
Stable releases (without pre-release suffixes) are automatically published to crates.io.
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
1. Set `CARGO_REGISTRY_TOKEN` secret in GitHub repository settings
|
||||||
|
2. Ensure you have publishing permissions for the crate
|
||||||
|
|
||||||
|
### Manual Publishing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dry run
|
||||||
|
cargo publish --dry-run
|
||||||
|
|
||||||
|
# Publish
|
||||||
|
cargo publish
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Release Workflow Fails
|
||||||
|
|
||||||
|
1. Check the Actions tab in GitHub for detailed logs
|
||||||
|
2. Common issues:
|
||||||
|
- Version mismatch between tag and Cargo.toml
|
||||||
|
- Tests failing on specific platforms
|
||||||
|
- Missing secrets (CARGO_REGISTRY_TOKEN, DOCKERHUB credentials)
|
||||||
|
|
||||||
|
### Version Already Exists
|
||||||
|
|
||||||
|
If you need to recreate a release:
|
||||||
|
|
||||||
|
1. Delete the tag: `git tag -d v1.2.3 && git push origin :v1.2.3`
|
||||||
|
2. Delete the GitHub release (if created)
|
||||||
|
3. Create the tag again
|
||||||
|
|
||||||
|
### Docker Build Fails
|
||||||
|
|
||||||
|
1. Check if all dependencies are available in the Docker environment
|
||||||
|
2. Verify Dockerfile syntax and build context
|
||||||
|
3. Test locally: `docker build -t docx-mcp:test .`
|
||||||
|
|
||||||
|
### crates.io Publishing Fails
|
||||||
|
|
||||||
|
1. Verify `CARGO_REGISTRY_TOKEN` is set and valid
|
||||||
|
2. Check if version already exists
|
||||||
|
3. Ensure all required metadata is in Cargo.toml
|
||||||
|
4. Run `cargo package --dry-run` to check for issues
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Signing Releases
|
||||||
|
|
||||||
|
Currently, releases are not cryptographically signed. Consider adding:
|
||||||
|
|
||||||
|
1. GPG signing of Git tags
|
||||||
|
2. Binary signing with platform-specific tools
|
||||||
|
3. SBOM (Software Bill of Materials) generation
|
||||||
|
|
||||||
|
### Supply Chain Security
|
||||||
|
|
||||||
|
- Dependencies are audited in CI with `cargo audit`
|
||||||
|
- Docker images use specific base image versions
|
||||||
|
- Build reproducibility is enhanced with Rust's deterministic builds
|
||||||
|
|
||||||
|
## Release Checklist
|
||||||
|
|
||||||
|
Use this checklist for important releases:
|
||||||
|
|
||||||
|
- [ ] All planned features are implemented
|
||||||
|
- [ ] All tests pass locally and in CI
|
||||||
|
- [ ] Documentation is updated
|
||||||
|
- [ ] Breaking changes are documented
|
||||||
|
- [ ] Migration guide is provided (for major releases)
|
||||||
|
- [ ] Security implications are reviewed
|
||||||
|
- [ ] Performance regression tests pass
|
||||||
|
- [ ] Cross-platform compatibility verified
|
||||||
|
- [ ] Release notes are prepared
|
||||||
|
|
||||||
|
## Post-Release Tasks
|
||||||
|
|
||||||
|
After a release:
|
||||||
|
|
||||||
|
1. **Verify Installation**: Test installation from released binaries
|
||||||
|
2. **Update Examples**: Update example configurations if needed
|
||||||
|
3. **Notify Users**: Announce significant releases
|
||||||
|
4. **Monitor Issues**: Watch for issues related to the new release
|
||||||
|
5. **Update Dependencies**: Consider updating dependent projects
|
||||||
|
|
||||||
|
## Emergency Releases
|
||||||
|
|
||||||
|
For critical security fixes:
|
||||||
|
|
||||||
|
1. Create a hotfix branch from the affected release tag
|
||||||
|
2. Apply minimal fix
|
||||||
|
3. Follow expedited release process
|
||||||
|
4. Consider yanking affected versions from crates.io if necessary
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Yank a version from crates.io (if needed)
|
||||||
|
cargo yank --version 1.2.3
|
||||||
|
|
||||||
|
# Un-yank if needed later
|
||||||
|
cargo yank --version 1.2.3 --undo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release Schedule
|
||||||
|
|
||||||
|
- **Patch releases**: As needed for bug fixes
|
||||||
|
- **Minor releases**: Monthly or when significant features accumulate
|
||||||
|
- **Major releases**: Annually or when breaking changes are necessary
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- Open an issue for release-related problems
|
||||||
|
- Check GitHub Actions logs for CI failures
|
||||||
|
- Review this guide and workflow files for automation details
|
||||||
@@ -169,20 +169,62 @@ flamegraph:
|
|||||||
changelog:
|
changelog:
|
||||||
git cliff --output CHANGELOG.md
|
git cliff --output CHANGELOG.md
|
||||||
|
|
||||||
# Prepare a release
|
# Release commands using the release script
|
||||||
|
|
||||||
|
# Create a patch release (0.1.0 -> 0.1.1)
|
||||||
|
release-patch:
|
||||||
|
./scripts/release.sh patch
|
||||||
|
|
||||||
|
# Create a minor release (0.1.0 -> 0.2.0)
|
||||||
|
release-minor:
|
||||||
|
./scripts/release.sh minor
|
||||||
|
|
||||||
|
# Create a major release (0.1.0 -> 1.0.0)
|
||||||
|
release-major:
|
||||||
|
./scripts/release.sh major
|
||||||
|
|
||||||
|
# Create a specific version release
|
||||||
|
release-version version:
|
||||||
|
./scripts/release.sh version {{version}}
|
||||||
|
|
||||||
|
# Dry run of patch release (see what would happen)
|
||||||
|
release-patch-dry:
|
||||||
|
./scripts/release.sh patch --dry-run
|
||||||
|
|
||||||
|
# Dry run of minor release
|
||||||
|
release-minor-dry:
|
||||||
|
./scripts/release.sh minor --dry-run
|
||||||
|
|
||||||
|
# Dry run of major release
|
||||||
|
release-major-dry:
|
||||||
|
./scripts/release.sh major --dry-run
|
||||||
|
|
||||||
|
# Dry run of specific version release
|
||||||
|
release-version-dry version:
|
||||||
|
./scripts/release.sh version {{version}} --dry-run
|
||||||
|
|
||||||
|
# Run all pre-release checks
|
||||||
|
release-check:
|
||||||
|
./scripts/release.sh check
|
||||||
|
|
||||||
|
# Generate changelog since last tag
|
||||||
|
release-changelog:
|
||||||
|
./scripts/release.sh changelog
|
||||||
|
|
||||||
|
# Create git tag for current version
|
||||||
|
release-tag:
|
||||||
|
./scripts/release.sh tag
|
||||||
|
|
||||||
|
# Prepare a release (legacy command - use release-* commands above)
|
||||||
prepare-release version:
|
prepare-release version:
|
||||||
# Update version in Cargo.toml
|
@echo "⚠️ This command is deprecated. Use 'just release-version {{version}}' instead."
|
||||||
sed -i 's/^version = ".*"/version = "{{version}}"/' Cargo.toml
|
@echo "The new release commands provide better automation and safety checks."
|
||||||
# Update dependencies to use new version
|
@echo ""
|
||||||
cargo update
|
@echo "Available release commands:"
|
||||||
# Run full test suite
|
@echo " just release-patch - Bump patch version"
|
||||||
just ci
|
@echo " just release-minor - Bump minor version"
|
||||||
# Generate changelog
|
@echo " just release-major - Bump major version"
|
||||||
just changelog
|
@echo " just release-version X.Y.Z - Set specific version"
|
||||||
# Commit changes
|
|
||||||
git add .
|
|
||||||
git commit -m "chore: prepare release {{version}}"
|
|
||||||
git tag -a "v{{version}}" -m "Release {{version}}"
|
|
||||||
|
|
||||||
# Show project statistics
|
# Show project statistics
|
||||||
stats:
|
stats:
|
||||||
@@ -221,4 +263,214 @@ init-hooks:
|
|||||||
# Remove git hooks
|
# Remove git hooks
|
||||||
remove-hooks:
|
remove-hooks:
|
||||||
rm -f .git/hooks/pre-commit
|
rm -f .git/hooks/pre-commit
|
||||||
echo "Pre-commit hook removed"
|
echo "Pre-commit hook removed"
|
||||||
|
|
||||||
|
# Docker commands
|
||||||
|
|
||||||
|
# Build multi-platform Docker image
|
||||||
|
docker-build-multiarch:
|
||||||
|
docker buildx create --use --name multiarch || true
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t docx-mcp:latest .
|
||||||
|
|
||||||
|
# Build and tag Docker image for release
|
||||||
|
docker-build-release version:
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 \
|
||||||
|
-t docx-mcp:{{version}} \
|
||||||
|
-t docx-mcp:latest \
|
||||||
|
-t ghcr.io/hongkongkiwi/docx-mcp:{{version}} \
|
||||||
|
-t ghcr.io/hongkongkiwi/docx-mcp:latest \
|
||||||
|
.
|
||||||
|
|
||||||
|
# Push Docker images to registry
|
||||||
|
docker-push version:
|
||||||
|
docker push docx-mcp:{{version}}
|
||||||
|
docker push docx-mcp:latest
|
||||||
|
docker push ghcr.io/hongkongkiwi/docx-mcp:{{version}}
|
||||||
|
docker push ghcr.io/hongkongkiwi/docx-mcp:latest
|
||||||
|
|
||||||
|
# Run Docker container with volume mount for testing
|
||||||
|
docker-test:
|
||||||
|
docker run --rm -it -v $(pwd)/test-docs:/test-docs docx-mcp:latest
|
||||||
|
|
||||||
|
# Development environment commands
|
||||||
|
|
||||||
|
# Full development setup from scratch
|
||||||
|
dev-setup:
|
||||||
|
# Install Rust if not present
|
||||||
|
@if ! command -v rustup >/dev/null 2>&1; then \
|
||||||
|
echo "Installing Rust..."; \
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; \
|
||||||
|
source ~/.cargo/env; \
|
||||||
|
fi
|
||||||
|
# Setup toolchain and tools
|
||||||
|
just setup
|
||||||
|
# Initialize git hooks
|
||||||
|
just init-hooks
|
||||||
|
# Build project
|
||||||
|
just build
|
||||||
|
echo "✅ Development environment ready!"
|
||||||
|
|
||||||
|
# Check system dependencies
|
||||||
|
check-deps:
|
||||||
|
@echo "=== System Dependencies Check ==="
|
||||||
|
@echo "Checking required tools..."
|
||||||
|
@command -v rustc >/dev/null && echo "✅ Rust compiler found" || echo "❌ Rust compiler not found"
|
||||||
|
@command -v cargo >/dev/null && echo "✅ Cargo found" || echo "❌ Cargo not found"
|
||||||
|
@command -v git >/dev/null && echo "✅ Git found" || echo "❌ Git not found"
|
||||||
|
@command -v docker >/dev/null && echo "✅ Docker found" || echo "❌ Docker not found"
|
||||||
|
@command -v just >/dev/null && echo "✅ Just found" || echo "❌ Just not found"
|
||||||
|
@echo ""
|
||||||
|
@echo "Optional tools:"
|
||||||
|
@command -v libreoffice >/dev/null && echo "✅ LibreOffice found" || echo "⚠️ LibreOffice not found (optional)"
|
||||||
|
@command -v pdftoppm >/dev/null && echo "✅ pdftoppm found" || echo "⚠️ pdftoppm not found (optional)"
|
||||||
|
@command -v convert >/dev/null && echo "✅ ImageMagick convert found" || echo "⚠️ ImageMagick not found (optional)"
|
||||||
|
|
||||||
|
# Cross-compilation commands
|
||||||
|
|
||||||
|
# Build for all supported targets
|
||||||
|
build-all-targets:
|
||||||
|
# Install targets if not present
|
||||||
|
rustup target add x86_64-unknown-linux-gnu
|
||||||
|
rustup target add x86_64-unknown-linux-musl
|
||||||
|
rustup target add aarch64-unknown-linux-gnu
|
||||||
|
rustup target add x86_64-apple-darwin
|
||||||
|
rustup target add aarch64-apple-darwin
|
||||||
|
rustup target add x86_64-pc-windows-msvc
|
||||||
|
# Build for each target
|
||||||
|
cargo build --release --target x86_64-unknown-linux-gnu --all-features
|
||||||
|
cargo build --release --target x86_64-unknown-linux-musl --all-features
|
||||||
|
cargo build --release --target x86_64-apple-darwin --all-features
|
||||||
|
@echo "✅ Built for all available targets"
|
||||||
|
|
||||||
|
# Build using cross for Linux targets
|
||||||
|
build-cross-linux:
|
||||||
|
cargo install cross --git https://github.com/cross-rs/cross
|
||||||
|
cross build --release --target x86_64-unknown-linux-gnu --all-features
|
||||||
|
cross build --release --target x86_64-unknown-linux-musl --all-features
|
||||||
|
cross build --release --target aarch64-unknown-linux-gnu --all-features
|
||||||
|
cross build --release --target aarch64-unknown-linux-musl --all-features
|
||||||
|
|
||||||
|
# Maintenance commands
|
||||||
|
|
||||||
|
# Update all dependencies to latest versions
|
||||||
|
update-deps:
|
||||||
|
cargo update
|
||||||
|
cargo outdated --depth 1
|
||||||
|
|
||||||
|
# Check for security vulnerabilities and update
|
||||||
|
security-update:
|
||||||
|
cargo audit fix
|
||||||
|
cargo update
|
||||||
|
|
||||||
|
# Clean everything (including registry cache)
|
||||||
|
clean-all:
|
||||||
|
cargo clean
|
||||||
|
rm -rf ~/.cargo/registry/cache
|
||||||
|
rm -rf ~/.cargo/git/db
|
||||||
|
docker system prune -f
|
||||||
|
|
||||||
|
# Backup project (excluding target and build artifacts)
|
||||||
|
backup:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
BACKUP_NAME="docx-mcp-backup-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
tar czf "${BACKUP_NAME}.tar.gz" \
|
||||||
|
--exclude='target' \
|
||||||
|
--exclude='.git' \
|
||||||
|
--exclude='*.log' \
|
||||||
|
--exclude='*.tmp' \
|
||||||
|
.
|
||||||
|
echo "✅ Backup created: ${BACKUP_NAME}.tar.gz"
|
||||||
|
|
||||||
|
# Development workflows
|
||||||
|
|
||||||
|
# Quick development loop (format, build, test unit, lint)
|
||||||
|
dev-loop:
|
||||||
|
just fmt
|
||||||
|
just build
|
||||||
|
just test-unit
|
||||||
|
just clippy
|
||||||
|
|
||||||
|
# Full quality check (everything CI runs)
|
||||||
|
quality-check:
|
||||||
|
just fmt-check
|
||||||
|
just clippy
|
||||||
|
just test
|
||||||
|
just docs-check
|
||||||
|
just audit
|
||||||
|
just deny
|
||||||
|
|
||||||
|
# Continuous development with file watching
|
||||||
|
dev-watch:
|
||||||
|
cargo install cargo-watch
|
||||||
|
cargo watch -w src -w tests -x "build" -x "test --lib"
|
||||||
|
|
||||||
|
# Performance analysis
|
||||||
|
perf-analysis:
|
||||||
|
# Build optimized release
|
||||||
|
cargo build --release --all-features
|
||||||
|
# Run criterion benchmarks
|
||||||
|
cargo bench --all-features
|
||||||
|
# Generate flamegraph if available
|
||||||
|
@if command -v flamegraph >/dev/null 2>&1; then \
|
||||||
|
echo "Generating flamegraph..."; \
|
||||||
|
cargo flamegraph --bin docx-mcp -- --help; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MCP-specific commands
|
||||||
|
|
||||||
|
# Test MCP server functionality
|
||||||
|
test-mcp:
|
||||||
|
@echo "Testing MCP server..."
|
||||||
|
# Build the server
|
||||||
|
cargo build --release --all-features
|
||||||
|
# Run basic functionality test
|
||||||
|
python3 example/test_client.py || echo "❌ MCP test failed"
|
||||||
|
|
||||||
|
# Generate MCP documentation
|
||||||
|
mcp-docs:
|
||||||
|
@echo "Generating MCP server documentation..."
|
||||||
|
cargo run --bin docx-mcp -- --help > docs/CLI_REFERENCE.md
|
||||||
|
@echo "✅ CLI reference updated"
|
||||||
|
|
||||||
|
# Example commands
|
||||||
|
|
||||||
|
# Run all examples
|
||||||
|
run-examples:
|
||||||
|
@echo "Running all examples..."
|
||||||
|
@if [ -f example/test_client.py ]; then python3 example/test_client.py; fi
|
||||||
|
@if [ -f example/automation_example.py ]; then python3 example/automation_example.py; fi
|
||||||
|
|
||||||
|
# Generate test documents
|
||||||
|
gen-test-docs:
|
||||||
|
@echo "Generating test documents..."
|
||||||
|
mkdir -p test-docs
|
||||||
|
# You could add commands here to generate various test DOCX files
|
||||||
|
|
||||||
|
# Utility commands
|
||||||
|
|
||||||
|
# Show detailed project info
|
||||||
|
info:
|
||||||
|
@echo "=== Project Information ==="
|
||||||
|
@echo "Name: docx-mcp"
|
||||||
|
@echo "Version: $(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')"
|
||||||
|
@echo "Rust version: $(rustc --version)"
|
||||||
|
@echo "Cargo version: $(cargo --version)"
|
||||||
|
@echo ""
|
||||||
|
just stats
|
||||||
|
|
||||||
|
# List all available commands with descriptions
|
||||||
|
help:
|
||||||
|
@echo "=== Available Commands ==="
|
||||||
|
@just --list
|
||||||
|
@echo ""
|
||||||
|
@echo "=== Release Commands ==="
|
||||||
|
@echo " release-patch - Create patch release (0.1.0 -> 0.1.1)"
|
||||||
|
@echo " release-minor - Create minor release (0.1.0 -> 0.2.0)"
|
||||||
|
@echo " release-major - Create major release (0.1.0 -> 1.0.0)"
|
||||||
|
@echo " release-version X - Create specific version release"
|
||||||
|
@echo " release-*-dry - Dry run versions of above commands"
|
||||||
|
@echo ""
|
||||||
|
@echo "=== Development Workflows ==="
|
||||||
|
@echo " dev-loop - Quick development cycle"
|
||||||
|
@echo " quality-check - Full quality assessment"
|
||||||
|
@echo " dev-setup - Complete development environment setup"
|
||||||
Executable
+355
@@ -0,0 +1,355 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Release script for docx-mcp
|
||||||
|
# This script helps with version management and release preparation
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
info() {
|
||||||
|
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
success() {
|
||||||
|
echo -e "${GREEN}✅ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
warning() {
|
||||||
|
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}❌ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we're in a git repository
|
||||||
|
check_git_repo() {
|
||||||
|
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||||
|
error "Not in a git repository"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if working directory is clean
|
||||||
|
check_clean_working_dir() {
|
||||||
|
if ! git diff-index --quiet HEAD --; then
|
||||||
|
error "Working directory is not clean. Please commit or stash your changes."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get current version from Cargo.toml
|
||||||
|
get_current_version() {
|
||||||
|
grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update version in Cargo.toml
|
||||||
|
update_version() {
|
||||||
|
local new_version=$1
|
||||||
|
info "Updating version to $new_version"
|
||||||
|
|
||||||
|
# Update Cargo.toml
|
||||||
|
sed -i.bak "s/^version = \".*\"/version = \"$new_version\"/" Cargo.toml
|
||||||
|
rm Cargo.toml.bak
|
||||||
|
|
||||||
|
# Update Cargo.lock
|
||||||
|
cargo update -p docx-mcp
|
||||||
|
|
||||||
|
success "Version updated to $new_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate changelog since last tag
|
||||||
|
generate_changelog() {
|
||||||
|
local last_tag=$(git tag --sort=-version:refname | head -1)
|
||||||
|
local new_version=$1
|
||||||
|
|
||||||
|
info "Generating changelog since $last_tag"
|
||||||
|
|
||||||
|
if [ -n "$last_tag" ]; then
|
||||||
|
git log --pretty=format:"- %s (%h)" --no-merges ${last_tag}..HEAD > CHANGELOG.tmp
|
||||||
|
else
|
||||||
|
git log --pretty=format:"- %s (%h)" --no-merges > CHANGELOG.tmp
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "## Release $new_version ($(date +%Y-%m-%d))"
|
||||||
|
echo ""
|
||||||
|
cat CHANGELOG.tmp
|
||||||
|
echo ""
|
||||||
|
rm CHANGELOG.tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run pre-release checks
|
||||||
|
run_checks() {
|
||||||
|
info "Running pre-release checks..."
|
||||||
|
|
||||||
|
# Format check
|
||||||
|
info "Checking code formatting..."
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
success "Code formatting is correct"
|
||||||
|
|
||||||
|
# Clippy check
|
||||||
|
info "Running Clippy..."
|
||||||
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
success "Clippy checks passed"
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
info "Running tests..."
|
||||||
|
cargo test --all-features
|
||||||
|
success "All tests passed"
|
||||||
|
|
||||||
|
# Build check
|
||||||
|
info "Testing release build..."
|
||||||
|
cargo build --release --all-features
|
||||||
|
success "Release build successful"
|
||||||
|
|
||||||
|
# Package check
|
||||||
|
info "Testing package..."
|
||||||
|
cargo package --dry-run
|
||||||
|
success "Package validation passed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and push git tag
|
||||||
|
create_tag() {
|
||||||
|
local version=$1
|
||||||
|
local tag="v$version"
|
||||||
|
|
||||||
|
info "Creating git tag $tag"
|
||||||
|
|
||||||
|
# Create annotated tag
|
||||||
|
git tag -a "$tag" -m "Release $tag"
|
||||||
|
|
||||||
|
success "Created tag $tag"
|
||||||
|
|
||||||
|
# Ask if user wants to push
|
||||||
|
echo -n "Push tag to origin? [y/N]: "
|
||||||
|
read -r response
|
||||||
|
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
git push origin "$tag"
|
||||||
|
success "Tag pushed to origin"
|
||||||
|
else
|
||||||
|
warning "Tag not pushed. Remember to push it manually: git push origin $tag"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show usage information
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $0 [COMMAND] [OPTIONS]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
patch Bump patch version (0.1.0 -> 0.1.1)
|
||||||
|
minor Bump minor version (0.1.0 -> 0.2.0)
|
||||||
|
major Bump major version (0.1.0 -> 1.0.0)
|
||||||
|
version X.Y.Z Set specific version
|
||||||
|
check Run pre-release checks only
|
||||||
|
changelog Generate changelog since last tag
|
||||||
|
tag Create git tag for current version
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--dry-run Show what would be done without making changes
|
||||||
|
--no-checks Skip pre-release checks (not recommended)
|
||||||
|
--no-tag Don't create git tag
|
||||||
|
--help Show this help message
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 patch # Bump to next patch version
|
||||||
|
$0 version 1.0.0 # Set version to 1.0.0
|
||||||
|
$0 check # Run all pre-release checks
|
||||||
|
$0 patch --dry-run # Show what patch release would do
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse version bump type
|
||||||
|
bump_version() {
|
||||||
|
local current_version=$1
|
||||||
|
local bump_type=$2
|
||||||
|
|
||||||
|
# Split version into components
|
||||||
|
IFS='.' read -ra VERSION_PARTS <<< "$current_version"
|
||||||
|
local major=${VERSION_PARTS[0]}
|
||||||
|
local minor=${VERSION_PARTS[1]}
|
||||||
|
local patch=${VERSION_PARTS[2]}
|
||||||
|
|
||||||
|
case $bump_type in
|
||||||
|
"patch")
|
||||||
|
patch=$((patch + 1))
|
||||||
|
;;
|
||||||
|
"minor")
|
||||||
|
minor=$((minor + 1))
|
||||||
|
patch=0
|
||||||
|
;;
|
||||||
|
"major")
|
||||||
|
major=$((major + 1))
|
||||||
|
minor=0
|
||||||
|
patch=0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Invalid bump type: $bump_type"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "${major}.${minor}.${patch}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate version format
|
||||||
|
validate_version() {
|
||||||
|
local version=$1
|
||||||
|
if ! [[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
||||||
|
error "Invalid version format: $version"
|
||||||
|
error "Expected format: X.Y.Z or X.Y.Z-suffix"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
main() {
|
||||||
|
local command=$1
|
||||||
|
local dry_run=false
|
||||||
|
local no_checks=false
|
||||||
|
local no_tag=false
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--dry-run)
|
||||||
|
dry_run=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--no-checks)
|
||||||
|
no_checks=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--no-tag)
|
||||||
|
no_tag=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [ -z "$command" ]; then
|
||||||
|
command=$1
|
||||||
|
elif [ -z "$version_arg" ] && [ "$command" = "version" ]; then
|
||||||
|
version_arg=$1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if command provided
|
||||||
|
if [ -z "$command" ]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Basic checks
|
||||||
|
check_git_repo
|
||||||
|
|
||||||
|
if [ "$dry_run" = false ]; then
|
||||||
|
check_clean_working_dir
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_version=$(get_current_version)
|
||||||
|
info "Current version: $current_version"
|
||||||
|
|
||||||
|
case $command in
|
||||||
|
"patch"|"minor"|"major")
|
||||||
|
new_version=$(bump_version "$current_version" "$command")
|
||||||
|
;;
|
||||||
|
"version")
|
||||||
|
if [ -z "$version_arg" ]; then
|
||||||
|
error "Version argument required for 'version' command"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
new_version=$version_arg
|
||||||
|
validate_version "$new_version"
|
||||||
|
;;
|
||||||
|
"check")
|
||||||
|
run_checks
|
||||||
|
success "All pre-release checks passed!"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
"changelog")
|
||||||
|
generate_changelog "$current_version"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
"tag")
|
||||||
|
if [ "$dry_run" = true ]; then
|
||||||
|
info "Would create tag v$current_version"
|
||||||
|
else
|
||||||
|
create_tag "$current_version"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Unknown command: $command"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
info "New version will be: $new_version"
|
||||||
|
|
||||||
|
if [ "$dry_run" = true ]; then
|
||||||
|
warning "DRY RUN MODE - No changes will be made"
|
||||||
|
info "Would update version from $current_version to $new_version"
|
||||||
|
if [ "$no_checks" = false ]; then
|
||||||
|
info "Would run pre-release checks"
|
||||||
|
fi
|
||||||
|
if [ "$no_tag" = false ]; then
|
||||||
|
info "Would create git tag v$new_version"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Confirm with user
|
||||||
|
echo -n "Proceed with release $new_version? [y/N]: "
|
||||||
|
read -r response
|
||||||
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
warning "Release cancelled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run pre-release checks
|
||||||
|
if [ "$no_checks" = false ]; then
|
||||||
|
run_checks
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update version
|
||||||
|
update_version "$new_version"
|
||||||
|
|
||||||
|
# Commit version bump
|
||||||
|
git add Cargo.toml Cargo.lock
|
||||||
|
git commit -m "Release $new_version"
|
||||||
|
success "Version bump committed"
|
||||||
|
|
||||||
|
# Create tag
|
||||||
|
if [ "$no_tag" = false ]; then
|
||||||
|
create_tag "$new_version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate changelog for reference
|
||||||
|
info "Changelog for release:"
|
||||||
|
generate_changelog "$new_version"
|
||||||
|
|
||||||
|
success "Release $new_version completed!"
|
||||||
|
info "Next steps:"
|
||||||
|
info "1. Push commits: git push origin main"
|
||||||
|
if [ "$no_tag" = false ]; then
|
||||||
|
info "2. Push tag: git push origin v$new_version (if not done already)"
|
||||||
|
fi
|
||||||
|
info "3. GitHub Actions will automatically create the release"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function with all arguments
|
||||||
|
main "$@"
|
||||||
+4
-2
@@ -3,6 +3,7 @@ use mcp_server::{Server, ServerBuilder, ServerOptions};
|
|||||||
use mcp_core::ToolManager;
|
use mcp_core::ToolManager;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
|
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
mod docx_tools;
|
mod docx_tools;
|
||||||
mod docx_handler;
|
mod docx_handler;
|
||||||
@@ -23,8 +24,9 @@ async fn main() -> Result<()> {
|
|||||||
.with(EnvFilter::from_default_env())
|
.with(EnvFilter::from_default_env())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
// Load security configuration from environment
|
// Parse command line arguments (which also includes environment variables)
|
||||||
let security_config = security::SecurityConfig::from_env();
|
let args = security::Args::parse();
|
||||||
|
let security_config = security::SecurityConfig::from_args(args);
|
||||||
info!("Starting DOCX MCP Server - Security: {}", security_config.get_summary());
|
info!("Starting DOCX MCP Server - Security: {}", security_config.get_summary());
|
||||||
|
|
||||||
let docx_provider = DocxToolsProvider::new_with_security(security_config);
|
let docx_provider = DocxToolsProvider::new_with_security(security_config);
|
||||||
|
|||||||
+93
-1
@@ -2,6 +2,46 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
/// Command line arguments for the DOCX MCP server
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(name = "docx-mcp")]
|
||||||
|
#[command(about = "A comprehensive Model Context Protocol (MCP) server for Microsoft Word DOCX file manipulation")]
|
||||||
|
#[command(version)]
|
||||||
|
pub struct Args {
|
||||||
|
/// Enable readonly mode - only allow viewing operations
|
||||||
|
#[arg(long, env = "DOCX_MCP_READONLY")]
|
||||||
|
pub readonly: bool,
|
||||||
|
|
||||||
|
/// Comma-separated whitelist of allowed commands
|
||||||
|
#[arg(long, env = "DOCX_MCP_WHITELIST", value_delimiter = ',')]
|
||||||
|
pub whitelist: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Comma-separated blacklist of forbidden commands
|
||||||
|
#[arg(long, env = "DOCX_MCP_BLACKLIST", value_delimiter = ',')]
|
||||||
|
pub blacklist: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Enable sandbox mode - restrict file operations to temp directory only
|
||||||
|
#[arg(long, env = "DOCX_MCP_SANDBOX")]
|
||||||
|
pub sandbox: bool,
|
||||||
|
|
||||||
|
/// Disable external tools (LibreOffice, etc.)
|
||||||
|
#[arg(long, env = "DOCX_MCP_NO_EXTERNAL_TOOLS")]
|
||||||
|
pub no_external_tools: bool,
|
||||||
|
|
||||||
|
/// Disable network operations
|
||||||
|
#[arg(long, env = "DOCX_MCP_NO_NETWORK")]
|
||||||
|
pub no_network: bool,
|
||||||
|
|
||||||
|
/// Maximum document size in bytes
|
||||||
|
#[arg(long, env = "DOCX_MCP_MAX_SIZE")]
|
||||||
|
pub max_size: Option<usize>,
|
||||||
|
|
||||||
|
/// Maximum number of open documents
|
||||||
|
#[arg(long, env = "DOCX_MCP_MAX_DOCS")]
|
||||||
|
pub max_docs: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Security configuration for the MCP server
|
/// Security configuration for the MCP server
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -47,7 +87,59 @@ impl Default for SecurityConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SecurityConfig {
|
impl SecurityConfig {
|
||||||
/// Load configuration from environment variables
|
/// Create configuration from command line arguments
|
||||||
|
pub fn from_args(args: Args) -> Self {
|
||||||
|
let mut config = Self::default();
|
||||||
|
|
||||||
|
// Apply command line arguments
|
||||||
|
if args.readonly {
|
||||||
|
config.readonly_mode = true;
|
||||||
|
info!("Running in READONLY mode - only viewing operations allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(whitelist) = args.whitelist {
|
||||||
|
let commands: HashSet<String> = whitelist.into_iter().collect();
|
||||||
|
info!("Command whitelist enabled with {} commands", commands.len());
|
||||||
|
config.command_whitelist = Some(commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(blacklist) = args.blacklist {
|
||||||
|
let commands: HashSet<String> = blacklist.into_iter().collect();
|
||||||
|
info!("Command blacklist enabled with {} blocked commands", commands.len());
|
||||||
|
config.command_blacklist = Some(commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.sandbox {
|
||||||
|
config.sandbox_mode = true;
|
||||||
|
config.allow_external_tools = false;
|
||||||
|
config.allow_network = false;
|
||||||
|
info!("Running in SANDBOX mode - restricted file operations");
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.no_external_tools {
|
||||||
|
config.allow_external_tools = false;
|
||||||
|
info!("External tools disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.no_network {
|
||||||
|
config.allow_network = false;
|
||||||
|
info!("Network operations disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(size) = args.max_size {
|
||||||
|
config.max_document_size = size;
|
||||||
|
info!("Max document size set to {} bytes", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(max) = args.max_docs {
|
||||||
|
config.max_open_documents = max;
|
||||||
|
info!("Max open documents set to {}", max);
|
||||||
|
}
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load configuration from environment variables (deprecated, use from_args instead)
|
||||||
pub fn from_env() -> Self {
|
pub fn from_env() -> Self {
|
||||||
let mut config = Self::default();
|
let mut config = Self::default();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user