Docker Release (Manual) #11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Docker Release (Manual) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version_bump: | |
| description: 'Version bump type' | |
| required: true | |
| default: 'auto' | |
| type: choice | |
| options: | |
| - auto | |
| - major | |
| - minor | |
| - patch | |
| - none | |
| dry_run: | |
| description: 'Dry run (build only, do not push)' | |
| required: false | |
| default: false | |
| type: boolean | |
| env: | |
| DOCKER_IMAGE_NAME: app | |
| DOCKER_HUB_USERNAME: withoutbg | |
| jobs: | |
| docker-release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js (for semver calculation) | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Calculate next version | |
| id: version | |
| run: | | |
| # Get the latest git tag | |
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") | |
| CURRENT_VERSION=${LATEST_TAG#v} | |
| echo "📌 Current version: $CURRENT_VERSION" | |
| # Parse version components | |
| MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1) | |
| MINOR=$(echo $CURRENT_VERSION | cut -d. -f2) | |
| PATCH=$(echo $CURRENT_VERSION | cut -d. -f3) | |
| # Determine next version based on input | |
| BUMP_TYPE="${{ inputs.version_bump }}" | |
| if [ "$BUMP_TYPE" = "none" ]; then | |
| NEW_VERSION="$CURRENT_VERSION" | |
| echo "ℹ️ Using current version (no bump)" | |
| elif [ "$BUMP_TYPE" = "major" ]; then | |
| MAJOR=$((MAJOR + 1)) | |
| MINOR=0 | |
| PATCH=0 | |
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "⬆️ MAJOR bump: $CURRENT_VERSION → $NEW_VERSION" | |
| elif [ "$BUMP_TYPE" = "minor" ]; then | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "⬆️ MINOR bump: $CURRENT_VERSION → $NEW_VERSION" | |
| elif [ "$BUMP_TYPE" = "patch" ]; then | |
| PATCH=$((PATCH + 1)) | |
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "⬆️ PATCH bump: $CURRENT_VERSION → $NEW_VERSION" | |
| elif [ "$BUMP_TYPE" = "auto" ]; then | |
| # Auto-detect based on commit messages since last tag | |
| COMMITS_SINCE_TAG=$(git log ${LATEST_TAG}..HEAD --oneline --pretty=format:"%s" 2>/dev/null || echo "") | |
| if echo "$COMMITS_SINCE_TAG" | grep -qE "^feat!:|BREAKING CHANGE:"; then | |
| MAJOR=$((MAJOR + 1)) | |
| MINOR=0 | |
| PATCH=0 | |
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "🔍 Auto-detected MAJOR bump (breaking changes): $CURRENT_VERSION → $NEW_VERSION" | |
| elif echo "$COMMITS_SINCE_TAG" | grep -qE "^feat:"; then | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "🔍 Auto-detected MINOR bump (new features): $CURRENT_VERSION → $NEW_VERSION" | |
| elif echo "$COMMITS_SINCE_TAG" | grep -qE "^fix:|^perf:|^refactor:"; then | |
| PATCH=$((PATCH + 1)) | |
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "🔍 Auto-detected PATCH bump (fixes/improvements): $CURRENT_VERSION → $NEW_VERSION" | |
| else | |
| NEW_VERSION="$CURRENT_VERSION" | |
| echo "ℹ️ No version-worthy commits found, using current version: $NEW_VERSION" | |
| fi | |
| fi | |
| # Output version info | |
| VERSION="$NEW_VERSION" | |
| VERSION_MAJOR=$(echo $VERSION | cut -d. -f1) | |
| VERSION_MINOR=$(echo $VERSION | cut -d. -f1-2) | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| echo "MAJOR=$VERSION_MAJOR" >> $GITHUB_OUTPUT | |
| echo "MINOR=$VERSION_MINOR" >> $GITHUB_OUTPUT | |
| echo "" | |
| echo "📦 Building version: $VERSION" | |
| echo "🏷️ Tags: $VERSION, $VERSION_MINOR, $VERSION_MAJOR, latest" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ vars.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build and test Docker image locally | |
| run: | | |
| echo "🔨 Building Docker image for validation..." | |
| docker buildx build \ | |
| --platform linux/amd64 \ | |
| -f apps/web/Dockerfile \ | |
| -t ${{ env.DOCKER_IMAGE_NAME }}:test \ | |
| --load \ | |
| . | |
| echo "✅ Local build successful" | |
| echo "🧪 Testing Docker image..." | |
| docker run -d --name test-container -p 8080:80 ${{ env.DOCKER_IMAGE_NAME }}:test | |
| # Wait for container to start | |
| echo "⏳ Waiting for container to start..." | |
| sleep 10 | |
| # Test health endpoint | |
| echo "🏥 Testing health endpoint..." | |
| curl -f http://localhost:8080/api/health || (docker logs test-container && exit 1) | |
| echo "✅ Health check passed" | |
| # Cleanup | |
| docker stop test-container | |
| docker rm test-container | |
| echo "✅ Docker image validation complete" | |
| - name: Build and push multi-platform Docker image | |
| if: ${{ !inputs.dry_run }} | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: apps/web/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.MINOR }} | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.MAJOR }} | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Build multi-platform Docker image (dry run) | |
| if: ${{ inputs.dry_run }} | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: apps/web/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: false | |
| tags: | | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.MINOR }} | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.MAJOR }} | |
| ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Update version files and create tag | |
| if: ${{ !inputs.dry_run && inputs.version_bump != 'none' }} | |
| run: | | |
| VERSION="${{ steps.version.outputs.VERSION }}" | |
| echo "📝 Updating version files..." | |
| # Update backend pyproject.toml | |
| sed -i.bak "s/version = \".*\"/version = \"$VERSION\"/" apps/web/backend/pyproject.toml | |
| rm -f apps/web/backend/pyproject.toml.bak | |
| # Update frontend package.json | |
| sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" apps/web/frontend/package.json | |
| rm -f apps/web/frontend/package.json.bak | |
| # Configure git | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Commit and tag | |
| git add apps/web/backend/pyproject.toml apps/web/frontend/package.json | |
| git commit -m "chore(release): ${VERSION} [skip ci]" || echo "No changes to commit" | |
| git tag -a "v${VERSION}" -m "Release v${VERSION}" | |
| git push origin main --tags | |
| echo "✅ Version updated to $VERSION and tagged" | |
| - name: Summary | |
| if: ${{ !inputs.dry_run }} | |
| run: | | |
| echo "## 🚀 Docker Release Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ Multi-platform release **${{ steps.version.outputs.VERSION }}** complete!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📦 Built for platforms:" >> $GITHUB_STEP_SUMMARY | |
| echo "- linux/amd64 (Intel/AMD CPUs, most cloud servers)" >> $GITHUB_STEP_SUMMARY | |
| echo "- linux/arm64 (Apple Silicon, AWS Graviton, Raspberry Pi)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🏷️ Available tags:" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${{ steps.version.outputs.VERSION }}\` (full version)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${{ steps.version.outputs.MINOR }}\` (minor version)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${{ steps.version.outputs.MAJOR }}\` (major version)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`latest\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🐳 Pull and run:" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY | |
| echo "docker pull ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY | |
| echo "docker run -p 80:80 ${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📚 View on Docker Hub:" >> $GITHUB_STEP_SUMMARY | |
| echo "https://hub.docker.com/r/${{ env.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}" >> $GITHUB_STEP_SUMMARY | |
| - name: Summary (dry run) | |
| if: ${{ inputs.dry_run }} | |
| run: | | |
| echo "## 🧪 Docker Build Test Summary (Dry Run)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ Multi-platform build **${{ steps.version.outputs.VERSION }}** successful!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "⚠️ **Dry run mode**: Images were built but not pushed to Docker Hub" >> $GITHUB_STEP_SUMMARY | |
| echo "⚠️ **No version bump**: Version files and tags were not updated" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📦 Would have released:" >> $GITHUB_STEP_SUMMARY | |
| echo "- Version: **${{ steps.version.outputs.VERSION }}**" >> $GITHUB_STEP_SUMMARY | |
| echo "- Tags: \`${{ steps.version.outputs.VERSION }}\`, \`${{ steps.version.outputs.MINOR }}\`, \`${{ steps.version.outputs.MAJOR }}\`, \`latest\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📦 Validated for platforms:" >> $GITHUB_STEP_SUMMARY | |
| echo "- linux/amd64 (Intel/AMD CPUs)" >> $GITHUB_STEP_SUMMARY | |
| echo "- linux/arm64 (Apple Silicon, ARM servers)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "To push to Docker Hub, run the workflow without dry run mode." >> $GITHUB_STEP_SUMMARY | |