Build and push service images to multiple registries #35
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: Build and push service images to multiple registries | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| - 'tgo-*-v*' | |
| workflow_dispatch: | |
| inputs: | |
| services: | |
| description: 'Services to build (comma-separated, or leave empty for all). Example: tgo-api,tgo-web' | |
| required: false | |
| default: '' | |
| permissions: | |
| contents: read | |
| packages: write | |
| jobs: | |
| discover-services: | |
| name: Discover service directories under repos/ | |
| runs-on: ubuntu-latest | |
| outputs: | |
| services: ${{ steps.discover.outputs.services }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Discover services | |
| id: discover | |
| run: | | |
| set -euo pipefail | |
| echo "=== Service Discovery ===" | |
| echo "Trigger: ${{ github.event_name }}" | |
| echo "Ref: ${{ github.ref_name }}" | |
| echo "" | |
| # Priority 1: workflow_dispatch with specified services | |
| if [ -n "${{ github.event.inputs.services }}" ]; then | |
| services_input="${{ github.event.inputs.services }}" | |
| echo "✓ Using manually specified services: $services_input" | |
| json="[" | |
| first=1 | |
| for s in $(echo "$services_input" | tr ',' '\n'); do | |
| s=$(echo "$s" | xargs) # Remove leading/trailing whitespace | |
| if [ -z "$s" ]; then continue; fi | |
| if [ $first -eq 0 ]; then | |
| json="$json," | |
| fi | |
| json="$json\"$s\"" | |
| first=0 | |
| done | |
| json="$json]" | |
| echo "Services to build: $json" | |
| echo "services=$json" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Priority 2: Tag prefix (e.g., tgo-api-v1.0.0) | |
| tag="${{ github.ref_name }}" | |
| if [[ $tag =~ ^(tgo-[a-z-]+)-v ]]; then | |
| service="${BASH_REMATCH[1]}" | |
| echo "✓ Tag prefix detected: $tag" | |
| echo "✓ Building specific service: $service" | |
| echo "services=[\"$service\"]" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Priority 3: Auto-discover all services | |
| echo "✓ Auto-discovering all services..." | |
| if [ ! -d "repos" ]; then | |
| echo "repos directory not found" >&2 | |
| exit 1 | |
| fi | |
| services=() | |
| for dir in repos/*/; do | |
| [ -d "$dir" ] || continue | |
| name="${dir#repos/}" | |
| name="${name%/}" | |
| # Ensure the service has a Dockerfile | |
| if [ -f "repos/$name/Dockerfile" ]; then | |
| services+=("$name") | |
| fi | |
| done | |
| if [ "${#services[@]}" -eq 0 ]; then | |
| echo "No service directories with Dockerfile found under repos/" >&2 | |
| exit 1 | |
| fi | |
| json="[" | |
| first=1 | |
| for s in "${services[@]}"; do | |
| if [ $first -eq 0 ]; then | |
| json="$json," | |
| fi | |
| json="$json\"$s\"" | |
| first=0 | |
| done | |
| json="$json]" | |
| echo "Discovered services: $json" | |
| echo "services=$json" >> "$GITHUB_OUTPUT" | |
| build-and-push: | |
| name: Build and push Docker images | |
| needs: discover-services | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| service: ${{ fromJson(needs.discover-services.outputs.services) }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Display build information | |
| run: | | |
| echo "=== Build Information ===" | |
| echo "Service: ${{ matrix.service }}" | |
| echo "Tag/Version: ${{ github.ref_name }}" | |
| echo "Commit SHA: ${{ github.sha }}" | |
| echo "Repository: ${{ github.repository }}" | |
| echo "Trigger: ${{ github.event_name }}" | |
| echo "" | |
| - name: Set build metadata | |
| run: | | |
| echo "BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_ENV | |
| - name: Free up disk space | |
| if: matrix.service == 'tgo-rag' | |
| run: | | |
| echo "Disk space before cleanup:" | |
| df -h | |
| # Remove unnecessary pre-installed software | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /usr/local/lib/android | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /opt/hostedtoolcache/CodeQL | |
| sudo rm -rf /usr/local/share/boost | |
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" | |
| # Clean up apt cache | |
| sudo apt-get clean | |
| sudo apt-get autoremove -y | |
| sudo apt-get autoclean -y | |
| # Clean up Docker system | |
| docker system prune -af --volumes | |
| echo "Disk space after cleanup:" | |
| df -h | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Log in to Alibaba Cloud Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: registry.cn-shanghai.aliyuncs.com | |
| username: ${{ secrets.ALIYUN_REGISTRY_USERNAME }} | |
| password: ${{ secrets.ALIYUN_REGISTRY_PASSWORD }} | |
| - name: Build and push image for ${{ matrix.service }} | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: ./repos/${{ matrix.service }} | |
| file: ./repos/${{ matrix.service }}/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ matrix.service }}:${{ github.ref_name }} | |
| ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ matrix.service }}:latest | |
| tgoai/${{ matrix.service }}:${{ github.ref_name }} | |
| tgoai/${{ matrix.service }}:latest | |
| registry.cn-shanghai.aliyuncs.com/tgoai/${{ matrix.service }}:${{ github.ref_name }} | |
| registry.cn-shanghai.aliyuncs.com/tgoai/${{ matrix.service }}:latest | |
| labels: | | |
| org.opencontainers.image.source=${{ github.repository }} | |
| org.opencontainers.image.revision=${{ github.sha }} | |
| org.opencontainers.image.version=${{ github.ref_name }} | |
| org.opencontainers.image.title=${{ matrix.service }} | |
| build-args: | | |
| SERVICE_NAME=${{ matrix.service }} | |
| IMAGE_TAG=${{ github.ref_name }} | |
| GIT_SHA=${{ github.sha }} | |
| APP_VERSION=${{ github.ref_name }} | |
| GIT_COMMIT=${{ github.sha }} | |
| BUILD_TIME=${{ env.BUILD_TIME }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Verify multi-architecture manifest | |
| run: | | |
| echo "Verifying multi-architecture manifest for ${{ matrix.service }}..." | |
| # Check GHCR manifest | |
| docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ matrix.service }}:latest | |
| # Verify both architectures are present | |
| if docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ matrix.service }}:latest | grep -q "linux/amd64"; then | |
| echo "✓ AMD64 architecture found" | |
| else | |
| echo "✗ AMD64 architecture missing" | |
| exit 1 | |
| fi | |
| if docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ matrix.service }}:latest | grep -q "linux/arm64"; then | |
| echo "✓ ARM64 architecture found" | |
| else | |
| echo "✗ ARM64 architecture missing" | |
| exit 1 | |
| fi | |
| echo "Multi-architecture build verified successfully!" | |