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