ci.sh•9.26 kB
#!/bin/bash
# Time MCP Server - CI/CD Build Script
# This script builds, tests, and optionally deploys the Time MCP Server
set -e # Exit on any error
set -u # Exit on undefined variables
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
PROJECT_NAME="time-mcp-server"
DOCKER_IMAGE="time-mcp-server"
DOCKER_TAG="${DOCKER_TAG:-latest}"
BUILD_TYPE="${BUILD_TYPE:-local}" # local, docker, or all
RUN_TESTS="${RUN_TESTS:-true}"
# Functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_banner() {
echo -e "${BLUE}"
echo "=================================="
echo " Time MCP Server CI/CD Build"
echo "=================================="
echo -e "${NC}"
echo "Build Type: $BUILD_TYPE"
echo "Run Tests: $RUN_TESTS"
echo "Docker Tag: $DOCKER_TAG"
echo ""
}
check_prerequisites() {
log_info "Checking prerequisites..."
# Check if we're in the right directory
if [[ ! -f "package.json" ]]; then
log_error "package.json not found. Are you in the project root?"
exit 1
fi
# Check Node.js
if ! command -v node &> /dev/null; then
log_error "Node.js is not installed"
exit 1
fi
# Check npm
if ! command -v npm &> /dev/null; then
log_error "npm is not installed"
exit 1
fi
# Check Docker (if building Docker image)
if [[ "$BUILD_TYPE" == "docker" || "$BUILD_TYPE" == "all" ]]; then
if ! command -v docker &> /dev/null; then
log_error "Docker CLI is not installed"
log_error "Install with: brew install --cask docker"
exit 1
fi
# Check if Docker daemon is running
if ! docker ps &> /dev/null; then
log_error "Docker daemon is not running"
log_error "Start Docker Desktop: open -a Docker"
log_error "Wait for the whale icon to appear solid in your menu bar"
exit 1
fi
fi
log_success "Prerequisites check passed"
}
clean_build() {
log_info "Cleaning previous build artifacts..."
# Remove dist directory
if [[ -d "dist" ]]; then
rm -rf dist
log_info "Removed dist/ directory"
fi
# Remove node_modules if doing fresh install
if [[ "${FRESH_INSTALL:-false}" == "true" ]]; then
if [[ -d "node_modules" ]]; then
rm -rf node_modules
log_info "Removed node_modules/ directory"
fi
fi
log_success "Clean completed"
}
install_dependencies() {
log_info "Installing dependencies..."
# Use npm ci for reproducible builds in CI environments
if [[ "${CI:-false}" == "true" ]]; then
npm ci
else
npm install
fi
log_success "Dependencies installed"
}
build_typescript() {
log_info "Building TypeScript..."
# Compile TypeScript
npm run build
# Check if build was successful
if [[ ! -f "dist/index.js" ]]; then
log_error "Build failed - dist/index.js not found"
exit 1
fi
log_success "TypeScript build completed"
}
run_tests() {
if [[ "$RUN_TESTS" != "true" ]]; then
log_warning "Skipping tests (RUN_TESTS=false)"
return 0
fi
log_info "Running tests..."
# Check for timeout command and use appropriate method
if command -v timeout &> /dev/null; then
TIMEOUT_CMD="timeout 10s"
elif command -v gtimeout &> /dev/null; then
TIMEOUT_CMD="gtimeout 10s" # GNU coreutils on macOS
else
log_warning "No timeout command found, running tests without timeout"
TIMEOUT_CMD=""
fi
# Test that the server can list tools
log_info "Testing tool listing..."
TEST_OUTPUT=$(echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' | $TIMEOUT_CMD node dist/index.js 2>/dev/null)
if echo "$TEST_OUTPUT" | grep -q "get_current_time"; then
log_success "Tool listing test passed"
else
log_error "Tool listing test failed"
log_error "Output: $TEST_OUTPUT"
exit 1
fi
# Test getting current time
log_info "Testing get_current_time tool..."
TEST_OUTPUT=$(echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_current_time", "arguments": {}}}' | $TIMEOUT_CMD node dist/index.js 2>/dev/null)
if echo "$TEST_OUTPUT" | grep -q "Current time:"; then
log_success "get_current_time test passed"
else
log_error "get_current_time test failed"
log_error "Output: $TEST_OUTPUT"
exit 1
fi
# Test npm test script if it exists
if npm test &> /dev/null; then
log_success "npm test passed"
else
log_info "npm test not available or failed (this is OK)"
fi
log_success "All tests passed"
}
build_docker_image() {
if [[ "$BUILD_TYPE" != "docker" && "$BUILD_TYPE" != "all" ]]; then
log_info "Skipping Docker build (BUILD_TYPE=$BUILD_TYPE)"
return 0
fi
log_info "Building Docker image..."
# Build Docker image
docker build -t "${DOCKER_IMAGE}:${DOCKER_TAG}" .
# Tag as latest if not already
if [[ "$DOCKER_TAG" != "latest" ]]; then
docker tag "${DOCKER_IMAGE}:${DOCKER_TAG}" "${DOCKER_IMAGE}:latest"
fi
log_success "Docker image built: ${DOCKER_IMAGE}:${DOCKER_TAG}"
}
test_docker_image() {
if [[ "$BUILD_TYPE" != "docker" && "$BUILD_TYPE" != "all" ]]; then
return 0
fi
if [[ "$RUN_TESTS" != "true" ]]; then
return 0
fi
log_info "Testing Docker image..."
# Check for timeout command
if command -v timeout &> /dev/null; then
TIMEOUT_CMD="timeout 15s"
elif command -v gtimeout &> /dev/null; then
TIMEOUT_CMD="gtimeout 15s"
else
TIMEOUT_CMD=""
log_warning "Running Docker test without timeout"
fi
# Test that the Docker image works
TEST_OUTPUT=$(echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' | $TIMEOUT_CMD docker run --rm -i "${DOCKER_IMAGE}:${DOCKER_TAG}" 2>/dev/null)
if echo "$TEST_OUTPUT" | grep -q "get_current_time"; then
log_success "Docker image test passed"
else
log_error "Docker image test failed"
log_error "Output: $TEST_OUTPUT"
exit 1
fi
}
generate_build_info() {
log_info "Generating build information..."
# Create build info file
cat > build-info.json << EOF
{
"project": "$PROJECT_NAME",
"build_time": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"build_type": "$BUILD_TYPE",
"docker_tag": "$DOCKER_TAG",
"git_commit": "$(git rev-parse HEAD 2>/dev/null || echo 'unknown')",
"git_branch": "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')",
"node_version": "$(node --version)",
"npm_version": "$(npm --version)"
}
EOF
log_success "Build info generated: build-info.json"
}
print_summary() {
echo ""
echo -e "${GREEN}=================================="
echo " BUILD SUMMARY"
echo -e "==================================${NC}"
echo "Project: $PROJECT_NAME"
echo "Build Type: $BUILD_TYPE"
echo "Tests Run: $RUN_TESTS"
if [[ "$BUILD_TYPE" == "docker" || "$BUILD_TYPE" == "all" ]]; then
echo "Docker Image: ${DOCKER_IMAGE}:${DOCKER_TAG}"
fi
echo "Build completed at: $(date)"
echo ""
# Show next steps
echo -e "${BLUE}Next Steps:${NC}"
if [[ "$BUILD_TYPE" == "local" || "$BUILD_TYPE" == "all" ]]; then
echo "• Run locally: npm start"
echo "• Test: npm test"
fi
if [[ "$BUILD_TYPE" == "docker" || "$BUILD_TYPE" == "all" ]]; then
echo "• Run Docker: docker run -it ${DOCKER_IMAGE}:${DOCKER_TAG}"
echo "• Deploy: docker-compose up -d"
fi
echo ""
}
main() {
print_banner
check_prerequisites
clean_build
install_dependencies
build_typescript
run_tests
build_docker_image
test_docker_image
generate_build_info
print_summary
log_success "🎉 Build completed successfully!"
}
# Handle script arguments
case "${1:-}" in
--help|-h)
echo "Usage: $0 [options]"
echo ""
echo "Environment variables:"
echo " BUILD_TYPE Build type: local, docker, or all (default: local)"
echo " RUN_TESTS Run tests: true or false (default: true)"
echo " DOCKER_TAG Docker image tag (default: latest)"
echo " FRESH_INSTALL Remove node_modules before install (default: false)"
echo ""
echo "Examples:"
echo " $0 # Local build with tests"
echo " BUILD_TYPE=docker $0 # Docker build only"
echo " BUILD_TYPE=all $0 # Both local and Docker"
echo " RUN_TESTS=false $0 # Skip tests"
echo " DOCKER_TAG=v1.0.0 $0 # Custom Docker tag"
exit 0
;;
*)
main "$@"
;;
esac