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:
@@ -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
|
||||
Reference in New Issue
Block a user