Sync Docker Configs #1106
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: Sync Docker Configs | |
| on: | |
| schedule: | |
| - cron: '0 */6 * * *' # every 6 hours | |
| workflow_dispatch: | |
| # trigger actions manually | |
| push: | |
| branches: | |
| - main # run on PR merge to main | |
| jobs: | |
| sync-configs: | |
| runs-on: | |
| group: EC2 | |
| labels: [self-hosted, deploy] | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v2 | |
| # <NEW_CONFIG> | |
| # if adding new config, add step to create file here | |
| # <EXISTING_CONFIG> | |
| # if updating existing config, add new secrets to corresponding config file | |
| - name: Create Bot Config | |
| run: | | |
| mkdir -p configs | |
| cat > configs/bot_env.txt << EOF | |
| DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} | |
| SWECC_SERVER=${{ secrets.SWECC_SERVER }} | |
| ADMIN_CHANNEL=${{ secrets.ADMIN_CHANNEL }} | |
| LC_CHANNEL_ID=${{ secrets.LC_CHANNEL_ID }} | |
| TRANSCRIPTS_CHANNEL=${{ secrets.TRANSCRIPTS_CHANNEL }} | |
| SWECC_API_KEY=${{ secrets.SWECC_API_KEY }} | |
| SWECC_URL=${{ secrets.SWECC_URL }} | |
| PREFIX_COMMAND=${{ secrets.PREFIX_COMMAND }} | |
| SWECC_RESUME_CHANNEL=${{ secrets.SWECC_RESUME_CHANNEL }} | |
| CALENDAR_URL=${{ secrets.CALENDAR_URL }} | |
| AOC_LEADERBOARD_ID=${{ secrets.AOC_LEADERBOARD_ID }} | |
| AOC_SESSION=${{ secrets.AOC_SESSION }} | |
| AOC_LEADERBOARD_KEY=${{ secrets.AOC_LEADERBOARD_KEY }} | |
| INTERNSHIP_CHANNEL_ID=${{ secrets.INTERNSHIP_CHANNEL_ID }} | |
| NEW_GRAD_CHANNEL_ID=${{ secrets.NEW_GRAD_CHANNEL_ID }} | |
| GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} | |
| OFF_TOPIC_CHANNEL_ID=${{ secrets.OFF_TOPIC_CHANNEL_ID }} | |
| OFFICER_ROLE_ID=${{ secrets.OFFICER_ROLE_ID }} | |
| VERIFIED_ROLE_ID=${{ secrets.VERIFIED_ROLE_ID }} | |
| READING_GROUP_CHANNEL=${{ secrets.READING_GROUP_CHANNEL }} | |
| BOT_RABBIT_USER=${{ secrets.BOT_RABBIT_USER }} | |
| BOT_RABBIT_PASS=${{ secrets.BOT_RABBIT_PASS }} | |
| RABBIT_HOST=${{ secrets.RABBIT_HOST }} | |
| RABBIT_PORT=${{ secrets.RABBIT_PORT }} | |
| RABBIT_VHOST=${{ secrets.RABBIT_VHOST }} | |
| VERIFIED_EMAIL_ROLE_ID=${{ secrets.VERIFIED_EMAIL_ROLE_ID }} | |
| COHORT_CATEGORY_ID=${{ secrets.COHORT_CATEGORY_ID }} | |
| TIMELINE_CHANNEL=${{ secrets.TIMELINE_CHANNEL }} | |
| EOF | |
| - name: Create Chronos Config | |
| run: | | |
| cat > configs/chronos_env.txt << EOF | |
| AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| AWS_DEFAULT_REGION=${{ secrets.AWS_DEFAULT_REGION }} | |
| EOF | |
| - name: Create Scheduler Config | |
| run: | | |
| cat > configs/scheduler_env.txt << EOF | |
| ENV=prod | |
| SCHEDULER_API_KEY=${{ secrets.SCHEDULER_API_KEY }} | |
| EOF | |
| - name: Create Server Config | |
| run: | | |
| cat > configs/server_env.txt << EOF | |
| DJANGO_DEBUG=${{ secrets.DJANGO_DEBUG }} | |
| DB_HOST=${{ secrets.DB_HOST }} | |
| DB_NAME=${{ secrets.DB_NAME }} | |
| DB_PORT=${{ secrets.DB_PORT }} | |
| DB_USER=${{ secrets.DB_USER }} | |
| DB_PASSWORD=${{ secrets.DB_PASSWORD }} | |
| SENDGRID_API_KEY=${{ secrets.SENDGRID_API_KEY }} | |
| SUPABASE_URL=${{ secrets.SUPABASE_URL }} | |
| SUPABASE_KEY=${{ secrets.SUPABASE_KEY }} | |
| INTERNSHIP_CHANNEL_ID=${{ secrets.INTERNSHIP_CHANNEL_ID }} | |
| NEW_GRAD_CHANNEL_ID=${{ secrets.NEW_GRAD_CHANNEL_ID }} | |
| METRIC_SERVER_URL=${{ secrets.METRIC_SERVER_URL }} | |
| JWT_SECRET=${{ secrets.JWT_SECRET }} | |
| AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| AWS_BUCKET_NAME=${{ secrets.AWS_BUCKET_NAME }} | |
| RABBIT_HOST=${{ secrets.RABBIT_HOST }} | |
| RABBIT_PORT=${{ secrets.RABBIT_PORT }} | |
| RABBIT_VHOST=${{ secrets.RABBIT_VHOST }} | |
| SERVER_RABBIT_USER=${{ secrets.SERVER_RABBIT_USER }} | |
| SERVER_RABBIT_PASS=${{ secrets.SERVER_RABBIT_PASS }} | |
| EOF | |
| - name: Create Sockets Config | |
| run: | | |
| cat > configs/sockets_env.txt << EOF | |
| DB_HOST=${{ secrets.DB_HOST }} | |
| DB_NAME=${{ secrets.DB_NAME }} | |
| DB_PORT=${{ secrets.DB_PORT }} | |
| DB_USER=${{ secrets.DB_USER }} | |
| DB_PASSWORD=${{ secrets.DB_PASSWORD }} | |
| JWT_SECRET=${{ secrets.JWT_SECRET }} | |
| SOCKET_RABBIT_USER=${{ secrets.SOCKET_RABBIT_USER }} | |
| SOCKET_RABBIT_PASS=${{ secrets.SOCKET_RABBIT_PASS }} | |
| RABBIT_HOST=${{ secrets.RABBIT_HOST }} | |
| RABBIT_PORT=${{ secrets.RABBIT_PORT }} | |
| RABBIT_VHOST=${{ secrets.RABBIT_VHOST }} | |
| EOF | |
| - name: Create AI Config | |
| run: | | |
| cat > configs/ai_env.txt << EOF | |
| GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} | |
| AI_RABBIT_USER=${{ secrets.AI_RABBIT_USER }} | |
| AI_RABBIT_PASS=${{ secrets.AI_RABBIT_PASS }} | |
| AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| AWS_BUCKET_NAME=${{ secrets.AWS_BUCKET_NAME }} | |
| EOF | |
| - name: Extract and prepare configs for comparison | |
| run: | | |
| mkdir -p old_configs | |
| mkdir -p normalized_configs | |
| normalize_config() { | |
| local input_file=$1 | |
| local output_file=$2 | |
| if [ ! -f "${input_file}.original" ]; then | |
| cp "$input_file" "${input_file}.original" | |
| fi | |
| # remove empty lines, strip whitespace, sort | |
| grep '=' "$input_file" | grep -v '^[[:space:]]*$' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sort > "$output_file" | |
| } | |
| extract_config() { | |
| local config_name=$1 | |
| if docker config ls --filter name=$config_name -q | grep -q .; then | |
| docker config inspect $config_name --pretty | sed -n '/Data:/,$ p' | sed '1d' > "old_configs/${config_name}.raw" | |
| normalize_config "old_configs/${config_name}.raw" "normalized_configs/old_${config_name}.txt" | |
| else | |
| touch "normalized_configs/old_${config_name}.txt" | |
| fi | |
| } | |
| extract_config "bot_env" | |
| extract_config "chronos_env" | |
| extract_config "scheduler_env" | |
| extract_config "server_env" | |
| extract_config "sockets_env" | |
| extract_config "ai_env" | |
| for config in configs/*.txt; do | |
| filename=$(basename "$config") | |
| normalize_config "$config" "normalized_configs/new_${filename}" | |
| done | |
| - name: Detect changes and update configs | |
| id: config-changes | |
| run: | | |
| echo "SERVICES_TO_REDEPLOY=" > services_to_redeploy.txt | |
| check_and_update_config() { | |
| local config_name=$1 | |
| local old_normalized="normalized_configs/old_${config_name}.txt" | |
| local new_normalized="normalized_configs/new_${config_name}.txt" | |
| local original_config="configs/${config_name}.txt.original" | |
| # Use diff with no output for exact comparison | |
| if diff -q "$old_normalized" "$new_normalized" > /dev/null; then | |
| echo "No changes detected for ${config_name}" | |
| else | |
| echo "${config_name} has changed, updating Docker config..." | |
| echo "Differences found:" | |
| diff "$old_normalized" "$new_normalized" || true | |
| if docker config ls --filter name=$config_name -q | grep -q .; then | |
| docker config rm $config_name | |
| fi | |
| docker config create $config_name "$original_config" | |
| if grep -q "SERVICES_TO_REDEPLOY=$" services_to_redeploy.txt; then | |
| sed -i "s/SERVICES_TO_REDEPLOY=/SERVICES_TO_REDEPLOY=${config_name}/" services_to_redeploy.txt | |
| else | |
| sed -i "s/SERVICES_TO_REDEPLOY=/SERVICES_TO_REDEPLOY=${config_name},/" services_to_redeploy.txt | |
| fi | |
| fi | |
| } | |
| check_and_update_config "bot_env" | |
| check_and_update_config "chronos_env" | |
| check_and_update_config "scheduler_env" | |
| check_and_update_config "server_env" | |
| check_and_update_config "sockets_env" | |
| check_and_update_config "ai_env" | |
| source services_to_redeploy.txt | |
| if [ -n "$SERVICES_TO_REDEPLOY" ]; then | |
| echo "Services to redeploy: $SERVICES_TO_REDEPLOY" | |
| echo "CONFIG_CHANGED=true" >> $GITHUB_OUTPUT | |
| echo "SERVICES=$SERVICES_TO_REDEPLOY" >> $GITHUB_OUTPUT | |
| else | |
| echo "No config changes detected" | |
| echo "CONFIG_CHANGED=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Trigger Service Workflows | |
| if: steps.config-changes.outputs.CONFIG_CHANGED == 'true' | |
| run: | | |
| trigger_workflow() { | |
| local config_name=$1 | |
| local repo=$2 | |
| local workflow=$3 | |
| echo "Triggering workflow with new config: $config_name in repository $repo using workflow $workflow" | |
| curl -X POST \ | |
| -H "Authorization: token ${{ secrets.GH_TOKEN }}" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/$repo/actions/workflows/$workflow/dispatches" \ | |
| -d '{"ref":"main"}' | |
| # avoid rate limiting | |
| sleep 2 | |
| } | |
| CHANGED_SERVICES="${{ steps.config-changes.outputs.SERVICES }}" | |
| # TODO(elimelt): update workflow names | |
| if [[ "$CHANGED_SERVICES" == *"bot_env"* ]]; then | |
| trigger_workflow "bot_env" "swecc-uw/swecc-bot" "deploy.yml" | |
| fi | |
| if [[ "$CHANGED_SERVICES" == *"chronos_env"* ]]; then | |
| trigger_workflow "chronos_env" "swecc-uw/swecc-chronos" "deploy.yml" | |
| fi | |
| if [[ "$CHANGED_SERVICES" == *"scheduler_env"* ]]; then | |
| trigger_workflow "scheduler_env" "swecc-uw/swecc-scheduler" "deploy.yml" | |
| fi | |
| if [[ "$CHANGED_SERVICES" == *"server_env"* ]]; then | |
| trigger_workflow "server_env" "swecc-uw/swecc-server" "deploy.yml" | |
| fi | |
| if [[ "$CHANGED_SERVICES" == *"sockets_env"* ]]; then | |
| trigger_workflow "sockets_env" "swecc-uw/swecc-sockets" "deploy.yml" | |
| fi | |
| if [[ "$CHANGED_SERVICES" == *"ai_env"* ]]; then | |
| trigger_workflow "ai_env" "swecc-uw/swecc-ai" "deploy.yml" | |
| fi |