This commit is contained in:
286
scripts/ci/publish.sh
Executable file
286
scripts/ci/publish.sh
Executable file
@@ -0,0 +1,286 @@
|
||||
#!/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!"
|
||||
Reference in New Issue
Block a user