Deploy to Staging #3
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: Deploy to Staging | |
| on: | |
| workflow_run: | |
| workflows: [Build & Push Docker Image] | |
| types: [completed] | |
| branches: [main, master] | |
| workflow_dispatch: | |
| inputs: | |
| image_tag: | |
| description: 'Docker image tag to deploy (e.g. sha-abc1234)' | |
| required: false | |
| default: 'latest' | |
| concurrency: | |
| group: deploy-staging | |
| cancel-in-progress: false | |
| jobs: | |
| deploy-staging: | |
| name: Deploy to Staging | |
| runs-on: ubuntu-latest | |
| # Only run if the triggering workflow succeeded (or manual dispatch) | |
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| github.event.workflow_run.conclusion == 'success' | |
| environment: | |
| name: staging | |
| url: ${{ vars.STAGING_URL }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Resolve image tag | |
| id: tag | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "tag=${{ github.event.inputs.image_tag }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "tag=sha-$(echo ${{ github.event.workflow_run.head_sha }} | cut -c1-7)" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run database migrations | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.STAGING_HOST }} | |
| username: ${{ secrets.STAGING_USER }} | |
| key: ${{ secrets.STAGING_SSH_KEY }} | |
| script: | | |
| set -e | |
| echo "Running migrations on staging..." | |
| docker run --rm \ | |
| --network host \ | |
| -e BLINKS_DATABASE__URL="${{ secrets.STAGING_DATABASE_URL }}" \ | |
| -e RUN_ENV=staging \ | |
| ghcr.io/${{ github.repository_owner }}/blinks-backend:${{ steps.tag.outputs.tag }} \ | |
| /app/blinks-backend migrate | |
| echo "Migrations complete" | |
| - name: Deploy to staging server | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.STAGING_HOST }} | |
| username: ${{ secrets.STAGING_USER }} | |
| key: ${{ secrets.STAGING_SSH_KEY }} | |
| script: | | |
| set -e | |
| IMAGE="ghcr.io/${{ github.repository_owner }}/blinks-backend:${{ steps.tag.outputs.tag }}" | |
| echo "Pulling $IMAGE..." | |
| docker pull "$IMAGE" | |
| echo "Stopping existing container..." | |
| docker stop blinks-backend-staging 2>/dev/null || true | |
| docker rm blinks-backend-staging 2>/dev/null || true | |
| echo "Starting new container..." | |
| docker run -d \ | |
| --name blinks-backend-staging \ | |
| --restart unless-stopped \ | |
| -p 3000:3000 \ | |
| -e BLINKS_DATABASE__URL="${{ secrets.STAGING_DATABASE_URL }}" \ | |
| -e BLINKS_CACHE__REDIS_URL="${{ secrets.STAGING_REDIS_URL }}" \ | |
| -e BLINKS_QUEUE__REDIS_URL="${{ secrets.STAGING_REDIS_URL }}" \ | |
| -e BLINKS_JWT__SECRET="${{ secrets.STAGING_JWT_SECRET }}" \ | |
| -e BLINKS_ANCHOR__WEBHOOK_SECRET="${{ secrets.STAGING_ANCHOR_WEBHOOK_SECRET }}" \ | |
| -e BLINKS_OBSERVABILITY__SENTRY_DSN="${{ secrets.STAGING_SENTRY_DSN }}" \ | |
| -e RUN_ENV=staging \ | |
| -e LOG_FORMAT=json \ | |
| -e RUST_LOG=blinks_backend=info,tower_http=info \ | |
| "$IMAGE" | |
| echo "Deployed $IMAGE to staging" | |
| - name: Health check | |
| run: | | |
| echo "Waiting for service to be ready..." | |
| sleep 15 | |
| for i in {1..10}; do | |
| STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${{ vars.STAGING_URL }}/health" || echo "000") | |
| if [[ "$STATUS" == "200" ]]; then | |
| echo "Health check passed (attempt $i)" | |
| exit 0 | |
| fi | |
| echo "Attempt $i: got $STATUS, retrying in 10s..." | |
| sleep 10 | |
| done | |
| echo "Health check failed after 10 attempts" | |
| exit 1 | |
| - name: Notify deployment | |
| if: always() | |
| uses: slackapi/slack-github-action@v1.26.0 | |
| with: | |
| payload: | | |
| { | |
| "text": "${{ job.status == 'success' && '✅' || '❌' }} Staging deployment *${{ job.status }}*\nImage: `${{ steps.tag.outputs.tag }}`\nCommit: ${{ github.sha }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run>" | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK |