Create thoth-backend docker image and deploy it on VM #29
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: Create thoth-backend docker image and deploy it on VM | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| skip_backend_build: | |
| description: 'Skip build step (image already built)' | |
| required: false | |
| default: false | |
| type: boolean | |
| frontend_build_success: | |
| description: 'Frontend build succeeded' | |
| required: false | |
| default: true | |
| type: boolean | |
| frontend_version: | |
| description: 'Frontend version tag to deploy' | |
| required: false | |
| default: 'latest' | |
| type: string | |
| backend_version: | |
| description: 'Backend version tag to deploy' | |
| required: false | |
| default: 'latest' | |
| type: string | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| # Skip build if triggered by workflow_dispatch with skip_backend_build=true | |
| if: github.event.inputs.skip_backend_build != 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for Docker | |
| id: meta | |
| uses: docker/metadata-action@v4 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=schedule | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern=v{{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha | |
| - name: Build and push Docker image | |
| id: push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: | | |
| type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache | |
| cache-to: | | |
| type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max | |
| deploy: | |
| needs: build | |
| if: always() && (needs.build.result == 'success' || needs.build.result == 'skipped') && github.event.inputs.frontend_build_success != 'false' | |
| runs-on: ubuntu-latest | |
| # Define tags as job-level environment variables | |
| env: | |
| BACKEND_TAG: ${{ github.event.inputs.skip_backend_build == 'true' && (github.event.inputs.backend_version || 'latest') || github.ref_name }} | |
| FRONTEND_TAG: ${{ github.event.inputs.frontend_version || 'latest' }} | |
| permissions: | |
| contents: 'read' | |
| id-token: 'write' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Authenticate with Google Cloud Platform | |
| uses: 'google-github-actions/auth@v2' | |
| with: | |
| workload_identity_provider: 'projects/863738545301/locations/global/workloadIdentityPools/ubyssey-wif-pool-prd/providers/ubyssey-oidc-github-prd' | |
| service_account: 'deployment@ubyssey-prd.iam.gserviceaccount.com' | |
| - name: Configure SSH | |
| run: | | |
| mkdir -p ~/.ssh/ | |
| printf '%s\n' "$SSH_KEY" > ~/.ssh/production.key | |
| chmod 600 ~/.ssh/production.key | |
| cat >>~/.ssh/config <<END | |
| Host production | |
| HostName $SSH_HOST | |
| User $SSH_USER | |
| IdentityFile ~/.ssh/production.key | |
| StrictHostKeyChecking no | |
| END | |
| env: | |
| SSH_USER: github-actions-thoth | |
| SSH_KEY: ${{ secrets.GCP_SSH_PRIVATE_KEY_PRODUCTION }} | |
| SSH_HOST: ${{ secrets.GCP_SSH_HOST_PRODUCTION }} | |
| - name: Create thoth directory | |
| run: ssh production "mkdir -p /opt/thoth" | |
| - name: Validate Docker images | |
| run: | | |
| echo "Using backend tag: $BACKEND_TAG" | |
| echo "Using frontend tag: $FRONTEND_TAG" | |
| echo "Validating backend image: ghcr.io/ubyssey/thoth:$BACKEND_TAG" | |
| if docker manifest inspect ghcr.io/ubyssey/thoth:$BACKEND_TAG >/dev/null 2>&1; then | |
| echo "✅ Backend tag $BACKEND_TAG exists" | |
| else | |
| echo "❌ Backend tag $BACKEND_TAG not found" | |
| exit 1 | |
| fi | |
| echo "Validating frontend image: ghcr.io/ubyssey/thoth-frontend:$FRONTEND_TAG" | |
| if docker manifest inspect ghcr.io/ubyssey/thoth-frontend:$FRONTEND_TAG >/dev/null 2>&1; then | |
| echo "✅ Frontend tag $FRONTEND_TAG exists" | |
| else | |
| echo "❌ Frontend tag $FRONTEND_TAG not found" | |
| exit 1 | |
| fi | |
| - name: Set tags in docker-compose.yml | |
| run: | | |
| sed -i "s/\${BACKEND_TAG}/$BACKEND_TAG/g" docker-compose.yml | |
| sed -i "s/\${FRONTEND_TAG}/$FRONTEND_TAG/g" docker-compose.yml | |
| - name: Upload Docker Compose configuration | |
| run: scp docker-compose.yml production:/opt/thoth/docker-compose.yml | |
| - name: Pull Docker images for frontend and backend in parallel | |
| run: | | |
| ssh production "docker pull ghcr.io/ubyssey/thoth:$BACKEND_TAG" & | |
| ssh production "docker pull ghcr.io/ubyssey/thoth-frontend:$FRONTEND_TAG" & | |
| wait # Wait for both background processes to complete | |
| - name: Create shared network | |
| run: ssh production "docker network create --driver=overlay proxy-shared || true" | |
| - name: Deploy Thoth stack | |
| run: ssh production "docker stack deploy --compose-file /opt/thoth/docker-compose.yml thoth" | |
| build-failed: | |
| needs: build | |
| # Run only if deployment should be skipped due to build failures | |
| if: always() && ((needs.build.result == 'failure') || (github.event.inputs.frontend_build_success == 'false')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Report deployment failure | |
| run: | | |
| echo "=== Deployment Aborted ===" | |
| if [ "${{ needs.build.result }}" == "failure" ]; then | |
| echo "::error::Backend build failed - deployment aborted" | |
| fi | |
| if [ "${{ github.event.inputs.frontend_build_success }}" == "false" ]; then | |
| echo "::error::Frontend build failed - deployment aborted" | |
| fi | |
| echo "::error::Fix the build issues and try again" | |
| exit 1 |