Skip to content

Deploy

Deploy #17

Workflow file for this run

name: Deploy
on:
workflow_run:
workflows: ["CD"]
types: [completed]
branches: ['v*']
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'staging'
type: choice
options:
- staging
- production
version:
description: 'Version to deploy (e.g., v1.0.0)'
required: false
jobs:
prepare:
name: Prepare
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
outputs:
version: ${{ steps.version.outputs.version }}
is_tag: ${{ steps.version.outputs.is_tag }}
steps:
- name: Determine version
id: version
env:
INPUT_VERSION: ${{ inputs.version }}
EVENT_NAME: ${{ github.event_name }}
# workflow_run: tag is in head_branch
WR_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
run: |
IS_TAG="false"
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ -n "$INPUT_VERSION" ]; then
V="$INPUT_VERSION"
IS_TAG="true"
elif [ "$EVENT_NAME" = "workflow_run" ] && [ -n "$WR_HEAD_BRANCH" ]; then
V="$WR_HEAD_BRANCH"
IS_TAG="true"
else
V="latest"
fi
# Strip 'v' prefix — docker tags use semver without it
echo "version=${V#v}" >> "$GITHUB_OUTPUT"
echo "is_tag=${IS_TAG}" >> "$GITHUB_OUTPUT"
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: prepare
if: github.event.inputs.environment == 'staging'
environment: staging
steps:
- name: Deploy to Staging
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: |
cd ~/TJudge
export GITHUB_REPOSITORY_OWNER="4rh1t3ct0r7"
./scripts/deploy.sh staging ${{ needs.prepare.outputs.version }}
- name: Health check
env:
STAGING_URL: ${{ secrets.STAGING_URL }}
run: |
if [ -z "$STAGING_URL" ]; then
echo "STAGING_URL secret not set, skipping health check"
exit 0
fi
for i in $(seq 1 30); do
if curl -sf "$STAGING_URL/health"; then
echo "Health check passed"
exit 0
fi
echo "Waiting for health check... ($i/30)"
sleep 10
done
echo "Health check failed"
exit 1
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: prepare
if: github.event.inputs.environment == 'production' || (github.event_name == 'workflow_run' && needs.prepare.outputs.is_tag == 'true')
environment: production
steps:
- name: Deploy to production
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
envs: GHCR_TOKEN
script: |
cd ~/TJudge
git fetch origin && git pull origin main
echo "$GHCR_TOKEN" | docker login ghcr.io -u 4rh1t3ct0r7 --password-stdin
export GITHUB_REPOSITORY_OWNER="4rh1t3ct0r7"
export HOST_PROGRAMS_PATH="$(pwd)/data/programs"
mkdir -p data/programs
./scripts/init-secrets.sh
VERSION=${{ needs.prepare.outputs.version }} docker compose -f docker-compose.prod.yml pull
VERSION=${{ needs.prepare.outputs.version }} docker compose -f docker-compose.prod.yml up -d --remove-orphans
docker image prune -f
docker builder prune -f
env:
GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
- name: Health check production
env:
PRODUCTION_URL: ${{ secrets.PRODUCTION_URL }}
run: |
if [ -z "$PRODUCTION_URL" ]; then
echo "PRODUCTION_URL secret not set, skipping health check"
exit 0
fi
for i in $(seq 1 30); do
if curl -sf "$PRODUCTION_URL/health"; then
echo "Health check passed"
exit 0
fi
echo "Waiting for health check... ($i/30)"
sleep 10
done
echo "Health check failed"
exit 1
rollback:
name: Rollback Production
runs-on: ubuntu-latest
if: failure() && needs.deploy-production.result == 'failure'
needs: [prepare, deploy-production]
environment: production
steps:
- name: Rollback to previous version
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: |
cd ~/TJudge
./scripts/init-secrets.sh
docker compose -f docker-compose.prod.yml up -d --remove-orphans