Skip to content

Merge pull request #1429 from amandasmyths/main #73

Merge pull request #1429 from amandasmyths/main

Merge pull request #1429 from amandasmyths/main #73

name: Deploy to Staging
on:
push:
branches: [main]
workflow_dispatch:
inputs:
skip_smoke:
description: "Skip smoke tests (useful for infrastructure-only changes)"
required: false
default: "false"
type: boolean
concurrency:
group: staging-deploy
cancel-in-progress: false # Never cancel an in-flight staging deploy
permissions:
contents: read
deployments: write
jobs:
# ── Build and push Docker images ──────────────────────────────────────────
build-images:
name: Build & push images
runs-on: ubuntu-latest
outputs:
sha_short: ${{ steps.meta.outputs.sha_short }}
steps:
- uses: actions/checkout@v4
- name: Set image metadata
id: meta
run: echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & push backend image
uses: docker/build-push-action@v6
with:
context: ./backend
file: ./backend/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}/backend:staging
ghcr.io/${{ github.repository }}/backend:${{ steps.meta.outputs.sha_short }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build & push api image
uses: docker/build-push-action@v6
with:
context: ./api
file: ./api/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}/api:staging
ghcr.io/${{ github.repository }}/api:${{ steps.meta.outputs.sha_short }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build & push frontend image
uses: docker/build-push-action@v6
with:
context: ./frontend
file: ./frontend/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}/frontend:staging
ghcr.io/${{ github.repository }}/frontend:${{ steps.meta.outputs.sha_short }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ── Deploy to staging VM via SSH ──────────────────────────────────────────
deploy:
name: Deploy to staging
runs-on: ubuntu-latest
needs: build-images
environment:
name: staging
url: ${{ vars.STAGING_API_URL }}
steps:
- uses: actions/checkout@v4
- name: Create GitHub deployment
uses: chrnorm/deployment-action@v2
id: deployment
with:
token: ${{ secrets.GITHUB_TOKEN }}
environment: staging
ref: ${{ github.sha }}
- name: Copy compose file to staging server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_USER }}
key: ${{ secrets.STAGING_SSH_KEY }}
port: ${{ secrets.STAGING_SSH_PORT || 22 }}
source: "docker-compose.yml,docker-compose.override.yml,.env.testnet"
target: "~/swiftremit"
- name: Deploy via Docker Compose
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_USER }}
key: ${{ secrets.STAGING_SSH_KEY }}
port: ${{ secrets.STAGING_SSH_PORT || 22 }}
script: |
set -euo pipefail
cd ~/swiftremit
# Log into GHCR
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Export image tags so compose uses the freshly pushed images
export BACKEND_IMAGE=ghcr.io/${{ github.repository }}/backend:${{ needs.build-images.outputs.sha_short }}
export API_IMAGE=ghcr.io/${{ github.repository }}/api:${{ needs.build-images.outputs.sha_short }}
export FRONTEND_IMAGE=ghcr.io/${{ github.repository }}/frontend:${{ needs.build-images.outputs.sha_short }}
# Source testnet env overrides then deploy
set -a; source .env.testnet; set +a
docker compose pull
docker compose up -d --remove-orphans
# Give services a moment to start before smoke tests run
sleep 10
docker compose ps
- name: Mark deployment success
if: success()
uses: chrnorm/deployment-status@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
state: success
environment-url: ${{ vars.STAGING_API_URL }}
- name: Mark deployment failure
if: failure()
uses: chrnorm/deployment-status@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
state: failure
# ── Smoke tests against the live staging environment ──────────────────────
smoke-tests:
name: Smoke tests
runs-on: ubuntu-latest
needs: deploy
if: ${{ github.event.inputs.skip_smoke != 'true' }}
steps:
- uses: actions/checkout@v4
- name: Run smoke tests
env:
STAGING_API_URL: ${{ vars.STAGING_API_URL }}
STAGING_BACKEND_URL: ${{ vars.STAGING_BACKEND_URL }}
run: bash scripts/smoke-test-staging.sh
- name: Smoke test summary
if: always()
run: |
echo "### Smoke test results" >> $GITHUB_STEP_SUMMARY
echo "- API URL: \`${{ vars.STAGING_API_URL }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Backend URL: \`${{ vars.STAGING_BACKEND_URL }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Commit: \`${{ needs.build-images.outputs.sha_short }}\`" >> $GITHUB_STEP_SUMMARY