#!/bin/bash # scripts/ci/publish.sh - Публикация Docker образов в registry set -e echo "📤 Publishing Docker images to registry..." # Переменные REGISTRY=${DOCKER_REGISTRY:-"registry.hub.docker.com"} PROJECT_NAME="catlink" VERSION=${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}} DOCKER_USERNAME=${DOCKER_USERNAME} DOCKER_PASSWORD=${DOCKER_PASSWORD} echo "📋 Publish information:" echo " • Registry: $REGISTRY" echo " • Project: $PROJECT_NAME" echo " • Version: $VERSION" echo " • Username: ${DOCKER_USERNAME:0:3}***" # Проверка учетных данных if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_PASSWORD" ]; then echo "❌ Docker registry credentials not found!" echo "Please set DOCKER_USERNAME and DOCKER_PASSWORD environment variables" exit 1 fi # Вход в Docker registry echo "🔐 Authenticating with Docker registry..." echo "$DOCKER_PASSWORD" | docker login "$REGISTRY" -u "$DOCKER_USERNAME" --password-stdin if [ $? -ne 0 ]; then echo "❌ Failed to authenticate with Docker registry" exit 1 fi echo "✅ Successfully authenticated with registry" # Список образов для публикации IMAGES=( "$REGISTRY/$PROJECT_NAME-backend:$VERSION" "$REGISTRY/$PROJECT_NAME-backend:latest" "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" "$REGISTRY/$PROJECT_NAME-frontend:latest" ) # Проверка существования образов локально echo "🔍 Checking local images..." for image in "${IMAGES[@]}"; do if docker images --format "table {{.Repository}}:{{.Tag}}" | grep -q "${image#*/}"; then echo " ✅ Found: $image" else echo " ❌ Missing: $image" echo "Error: Local image $image not found. Please run build first." exit 1 fi done # Публикация образов echo "🚀 Publishing images..." for image in "${IMAGES[@]}"; do echo " • Publishing $image..." # Проверка размера образа size=$(docker images --format "table {{.Size}}" "$image" | tail -n +2) echo " Size: $size" # Публикация с повторными попытками for attempt in 1 2 3; do echo " Attempt $attempt/3..." if docker push "$image"; then echo " ✅ Successfully pushed $image" break else echo " ❌ Failed to push $image (attempt $attempt/3)" if [ $attempt -eq 3 ]; then echo "Error: Failed to push $image after 3 attempts" exit 1 fi sleep 5 fi done done # Проверка опубликованных образов echo "🔍 Verifying published images..." for image in "${IMAGES[@]}"; do echo " • Verifying $image..." # Попытка скачать manifest if docker manifest inspect "$image" > /dev/null 2>&1; then echo " ✅ Manifest verified for $image" else echo " ❌ Failed to verify manifest for $image" exit 1 fi done # Создание release notes echo "📝 Creating release notes..." cat > /tmp/release-notes.md << EOF # Release $VERSION ## 🚀 What's New ### Features - Updated to version $VERSION - Production-ready Docker images - Enhanced security configurations - Performance optimizations ### Technical Details - **Backend Image**: \`$REGISTRY/$PROJECT_NAME-backend:$VERSION\` - **Frontend Image**: \`$REGISTRY/$PROJECT_NAME-frontend:$VERSION\` - **Build Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC") - **Git Commit**: ${DRONE_COMMIT_SHA:-$(git rev-parse HEAD)} ### Docker Images \`\`\`bash # Pull latest images docker pull $REGISTRY/$PROJECT_NAME-backend:$VERSION docker pull $REGISTRY/$PROJECT_NAME-frontend:$VERSION # Or use latest tags docker pull $REGISTRY/$PROJECT_NAME-backend:latest docker pull $REGISTRY/$PROJECT_NAME-frontend:latest \`\`\` ### Quick Start \`\`\`bash # Download and run with docker-compose curl -sSL https://raw.githubusercontent.com/smartsoltech/links/main/docker-compose.production.yml -o docker-compose.yml docker-compose up -d \`\`\` ### Migration Notes - No breaking changes in this release - Database migrations included - Backward compatible ## 📊 Image Information | Component | Image | Size | Layers | |-----------|-------|------|--------| | Backend | \`$PROJECT_NAME-backend:$VERSION\` | $(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-backend:$VERSION" 2>/dev/null || echo "N/A") | $(docker history "$REGISTRY/$PROJECT_NAME-backend:$VERSION" 2>/dev/null | wc -l || echo "N/A") | | Frontend | \`$PROJECT_NAME-frontend:$VERSION\` | $(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" 2>/dev/null || echo "N/A") | $(docker history "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" 2>/dev/null | wc -l || echo "N/A") | ## 🔐 Security All images are scanned for vulnerabilities and follow security best practices: - Non-root user execution - Minimal base images - Regular security updates - Dependency vulnerability scanning ## 📖 Documentation - [Installation Guide](./docs/INSTALLATION.md) - [Configuration Guide](./docs/CONFIGURATION.md) - [API Documentation](./docs/API.md) - [Makefile Commands](./docs/MAKEFILE.md) --- **Full Changelog**: https://github.com/smartsoltech/links/compare/previous...${DRONE_TAG:-$VERSION} EOF # Уведомления о публикации echo "📢 Sending publication notifications..." # Slack уведомление (если настроено) if [ -n "$SLACK_WEBHOOK_URL" ]; then curl -X POST -H 'Content-type: application/json' \ --data "{ \"text\": \"🚀 *CatLink $VERSION* published successfully!\", \"attachments\": [ { \"color\": \"good\", \"fields\": [ { \"title\": \"Backend Image\", \"value\": \"\`$REGISTRY/$PROJECT_NAME-backend:$VERSION\`\", \"short\": true }, { \"title\": \"Frontend Image\", \"value\": \"\`$REGISTRY/$PROJECT_NAME-frontend:$VERSION\`\", \"short\": true }, { \"title\": \"Registry\", \"value\": \"\`$REGISTRY\`\", \"short\": true }, { \"title\": \"Build\", \"value\": \"#${DRONE_BUILD_NUMBER:-$(date +%s)}\", \"short\": true } ] } ] }" \ "$SLACK_WEBHOOK_URL" || echo "Failed to send Slack notification" fi # Discord уведомление (если настроено) if [ -n "$DISCORD_WEBHOOK_URL" ]; then curl -H "Content-Type: application/json" \ -d "{ \"embeds\": [ { \"title\": \"🚀 CatLink $VERSION Published\", \"color\": 65280, \"fields\": [ { \"name\": \"Backend Image\", \"value\": \"\`$REGISTRY/$PROJECT_NAME-backend:$VERSION\`\", \"inline\": true }, { \"name\": \"Frontend Image\", \"value\": \"\`$REGISTRY/$PROJECT_NAME-frontend:$VERSION\`\", \"inline\": true }, { \"name\": \"Registry\", \"value\": \"\`$REGISTRY\`\", \"inline\": true } ], \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" } ] }" \ "$DISCORD_WEBHOOK_URL" || echo "Failed to send Discord notification" fi # Создание статистики публикации echo "📊 Creating publication statistics..." cat > /tmp/publish-stats.json << EOF { "version": "$VERSION", "registry": "$REGISTRY", "project": "$PROJECT_NAME", "images": [ { "name": "$PROJECT_NAME-backend", "tag": "$VERSION", "size": "$(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-backend:$VERSION" 2>/dev/null || echo "unknown")", "published_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)" }, { "name": "$PROJECT_NAME-frontend", "tag": "$VERSION", "size": "$(docker images --format "{{.Size}}" "$REGISTRY/$PROJECT_NAME-frontend:$VERSION" 2>/dev/null || echo "unknown")", "published_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)" } ], "build_info": { "build_number": "${DRONE_BUILD_NUMBER:-unknown}", "commit_sha": "${DRONE_COMMIT_SHA:-$(git rev-parse HEAD 2>/dev/null || echo "unknown")}", "build_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "branch": "${DRONE_BRANCH:-$(git branch --show-current 2>/dev/null || echo "unknown")}" } } EOF # Очистка временных файлов echo "🧹 Cleaning up..." docker system prune -f > /dev/null 2>&1 || true # Выход из registry docker logout "$REGISTRY" > /dev/null 2>&1 || true echo "✅ Publication completed successfully!" echo "" echo "📦 Published Images:" echo " 🔗 Backend: $REGISTRY/$PROJECT_NAME-backend:$VERSION" echo " 🔗 Frontend: $REGISTRY/$PROJECT_NAME-frontend:$VERSION" echo "" echo "📄 Release notes: /tmp/release-notes.md" echo "📊 Statistics: /tmp/publish-stats.json" echo "" echo "🚀 Ready for deployment!"