Skip to content

chore: bump eafw_geomanager_web — migration 0034 universal scope helper #234

chore: bump eafw_geomanager_web — migration 0034 universal scope helper

chore: bump eafw_geomanager_web — migration 0034 universal scope helper #234

# EAFW FloodWatch - Build & Deploy to Staging
# Triggers on push to eafw branch
# Only builds images when their source files change
# Deploy is modular: only pulls + restarts services that were rebuilt
name: Build & Deploy to Staging
on:
push:
branches:
- eafw
workflow_dispatch:
inputs:
force_build:
description: 'Force rebuild all images'
type: boolean
default: false
deploy_only:
description: 'Skip build, deploy existing images by SHA'
type: string
default: ''
env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}
jobs:
detect-changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
cms: ${{ steps.changes.outputs.cms }}
mapviewer: ${{ steps.changes.outputs.mapviewer }}
mapserver: ${{ steps.changes.outputs.mapserver }}
mapcache: ${{ steps.changes.outputs.mapcache }}
jobs: ${{ steps.changes.outputs.jobs }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
submodules: false
- name: Check for changes
id: changes
run: |
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
echo "Changed files: $CHANGED"
# Match both regular file paths (eafw_geomanager_web/...) and bare
# submodule pointer changes (eafw_geomanager_web with no trailing slash)
if echo "$CHANGED" | grep -qE "^(eafw_geomanager_web|eafw_geomanager|eafw_georeport)(/|$)|^docker/cms/"; then
echo "cms=true" >> $GITHUB_OUTPUT
else
echo "cms=false" >> $GITHUB_OUTPUT
fi
if echo "$CHANGED" | grep -qE "^eafw_geomapviewer(/|$)|^docker/mapviewer/"; then
echo "mapviewer=true" >> $GITHUB_OUTPUT
else
echo "mapviewer=false" >> $GITHUB_OUTPUT
fi
if echo "$CHANGED" | grep -qE "^docker/mapserver/|^config/mapfiles/"; then
echo "mapserver=true" >> $GITHUB_OUTPUT
else
echo "mapserver=false" >> $GITHUB_OUTPUT
fi
if echo "$CHANGED" | grep -qE "^docker/mapcache/|^config/mapcache/"; then
echo "mapcache=true" >> $GITHUB_OUTPUT
else
echo "mapcache=false" >> $GITHUB_OUTPUT
fi
if echo "$CHANGED" | grep -qE "^docker/jobs/|^eafw_jobs/"; then
echo "jobs=true" >> $GITHUB_OUTPUT
else
echo "jobs=false" >> $GITHUB_OUTPUT
fi
build-cms:
name: Build CMS
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.cms == 'true' || github.event.inputs.force_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push CMS
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/cms/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/eafw-cms:latest
${{ env.IMAGE_PREFIX }}/eafw-cms:${{ github.sha }}
build-args: |
GH_TOKEN=${{ secrets.GH_PAT }}
CACHEBUST=${{ github.run_id }}
no-cache: true
build-mapviewer:
name: Build Mapviewer
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mapviewer == 'true' || github.event.inputs.force_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Mapviewer
uses: docker/build-push-action@v5
with:
context: ./eafw_geomapviewer
file: ./docker/mapviewer/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/eafw-mapviewer:latest
${{ env.IMAGE_PREFIX }}/eafw-mapviewer:${{ github.sha }}
build-args: |
CMS_API=/api
BASE_PATH=/
ASSET_PREFIX=
ADMIN_BOUNDARY_API=/pg/tileserv/
ANALYTICS_PROPERTY_ID=${{ secrets.STAGING_ANALYTICS_PROPERTY_ID }}
NEXT_TELEMETRY_DISABLED=1
build-mapserver:
name: Build Mapserver
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mapserver == 'true' || github.event.inputs.force_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Mapserver
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/mapserver/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/eafw-mapserver:latest
${{ env.IMAGE_PREFIX }}/eafw-mapserver:${{ github.sha }}
build-mapcache:
name: Build Mapcache
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mapcache == 'true' || github.event.inputs.force_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Mapcache
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/mapcache/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/eafw-mapcache:latest
${{ env.IMAGE_PREFIX }}/eafw-mapcache:${{ github.sha }}
build-jobs:
name: Build Jobs
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.jobs == 'true' || github.event.inputs.force_build == 'true'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Jobs
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/jobs/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/eafw-jobs:latest
${{ env.IMAGE_PREFIX }}/eafw-jobs:${{ github.sha }}
deploy:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: [detect-changes, build-cms, build-mapviewer, build-mapserver, build-mapcache, build-jobs]
if: always() && !cancelled()
environment: staging
steps:
- name: Determine which services to deploy
id: services
run: |
SERVICES=""
IMAGES=""
SHA="${{ github.sha }}"
PREFIX="${{ env.IMAGE_PREFIX }}"
FORCE="${{ github.event.inputs.force_build }}"
# A service needs deploy if it was rebuilt (job succeeded)
# or if force_build was requested
if [ "${{ needs.build-cms.result }}" = "success" ] || [ "$FORCE" = "true" ]; then
SERVICES="$SERVICES cms"
IMAGES="$IMAGES eafw-cms:$SHA"
fi
if [ "${{ needs.build-mapviewer.result }}" = "success" ] || [ "$FORCE" = "true" ]; then
SERVICES="$SERVICES mapviewer"
IMAGES="$IMAGES eafw-mapviewer:$SHA"
fi
if [ "${{ needs.build-mapserver.result }}" = "success" ] || [ "$FORCE" = "true" ]; then
SERVICES="$SERVICES mapserver"
IMAGES="$IMAGES eafw-mapserver:$SHA"
fi
if [ "${{ needs.build-mapcache.result }}" = "success" ] || [ "$FORCE" = "true" ]; then
SERVICES="$SERVICES mapcache"
IMAGES="$IMAGES eafw-mapcache:$SHA"
fi
if [ "${{ needs.build-jobs.result }}" = "success" ] || [ "$FORCE" = "true" ]; then
SERVICES="$SERVICES jobs"
IMAGES="$IMAGES eafw-jobs:$SHA"
fi
SERVICES=$(echo "$SERVICES" | xargs)
IMAGES=$(echo "$IMAGES" | xargs)
echo "services=$SERVICES" >> $GITHUB_OUTPUT
echo "images=$IMAGES" >> $GITHUB_OUTPUT
echo "sha=$SHA" >> $GITHUB_OUTPUT
echo "Will deploy: $SERVICES"
echo "Images: $IMAGES"
- name: Deploy to staging server
uses: appleboy/ssh-action@v1.0.3
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }}
FLOODPROOFS_SFTP_HOST: ${{ secrets.FLOODPROOFS_SFTP_HOST }}
FLOODPROOFS_SFTP_USER: ${{ secrets.FLOODPROOFS_SFTP_USER }}
FLOODPROOFS_SFTP_PASSWORD: ${{ secrets.FLOODPROOFS_SFTP_PASSWORD }}
WRF_FTP_HOST: ${{ secrets.WRF_FTP_HOST }}
WRF_FTP_USER: ${{ secrets.WRF_FTP_USER }}
WRF_FTP_PASSWORD: ${{ secrets.WRF_FTP_PASSWORD }}
FLOODS_API_KEY: ${{ secrets.FLOODS_API_KEY }}
SMTP_EMAIL_HOST: ${{ secrets.SMTP_EMAIL_HOST }}
SMTP_EMAIL_HOST_USER: ${{ secrets.SMTP_EMAIL_HOST_USER }}
SMTP_EMAIL_HOST_PASSWORD: ${{ secrets.SMTP_EMAIL_HOST_PASSWORD }}
DEPLOY_SERVICES: ${{ steps.services.outputs.services }}
DEPLOY_IMAGES: ${{ steps.services.outputs.images }}
DEPLOY_SHA: ${{ steps.services.outputs.sha }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.SSH_KEY }}
port: 22
command_timeout: 60m
envs: GH_TOKEN,DB_PASSWORD,DJANGO_SECRET_KEY,FLOODPROOFS_SFTP_HOST,FLOODPROOFS_SFTP_USER,FLOODPROOFS_SFTP_PASSWORD,WRF_FTP_HOST,WRF_FTP_USER,WRF_FTP_PASSWORD,FLOODS_API_KEY,SMTP_EMAIL_HOST,SMTP_EMAIL_HOST_USER,SMTP_EMAIL_HOST_PASSWORD,DEPLOY_SERVICES,DEPLOY_IMAGES,DEPLOY_SHA
script: |
set -e
cd ~
echo "========================================="
echo " STAGING DEPLOYMENT"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo " Services: ${DEPLOY_SERVICES:-none}"
echo " SHA: ${DEPLOY_SHA}"
echo "========================================="
# Login to GHCR
echo ${GH_TOKEN} | docker login ghcr.io -u icpac-igad --password-stdin
# Safe repo update — submodules are private repos under icpac-igad,
# so configure a per-user insteadOf rewrite that injects GH_TOKEN
# for any github.com URL the submodule machinery touches.
git config --global url."https://${GH_TOKEN}@github.com/".insteadOf "https://github.com/"
REPO_DIR=~/projects/flood_watch_system
if [ -d "$REPO_DIR/.git" ]; then
cd $REPO_DIR
git fetch origin
git reset --hard origin/eafw
else
mkdir -p ~/projects
git clone -b eafw https://${GH_TOKEN}@github.com/icpac-igad/flood_watch_system.git $REPO_DIR
cd $REPO_DIR
fi
# One-time aside: legacy tracked directories from before the
# submodule split linger on disk after a git reset --hard, blocking
# the fresh submodule clone. Move (don't delete) anything that
# isn't already a git checkout. Renaming the directory only needs
# write access on the *parent*, so root-owned children inside the
# legacy dir are preserved untouched in the .legacy backup.
TS=$(date +%Y%m%d_%H%M%S)
for sub in eafw_geomanager eafw_geomanager_web eafw_geomapviewer eafw_georeport; do
if [ -d "$sub" ] && [ ! -e "$sub/.git" ]; then
mv "$sub" "${sub}.legacy.${TS}"
echo " moved aside: $sub -> ${sub}.legacy.${TS}"
fi
done
# Sync + init/update every submodule to the SHA committed by the parent
git submodule sync --recursive
git submodule update --init --recursive --force
echo "Git SHA: $(git rev-parse --short HEAD)"
git submodule status
# Smart .env — create from template only on first deploy
if [ ! -f .env ]; then
cp .env.example .env
echo "FIRST DEPLOY: created .env from template"
fi
# Inject secrets
sed -i "s|^CMS_DB_PASSWORD=.*|CMS_DB_PASSWORD=${DB_PASSWORD}|" .env
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${DJANGO_SECRET_KEY}|" .env
sed -i "s|^FLOODPROOFS_SFTP_HOST=.*|FLOODPROOFS_SFTP_HOST=${FLOODPROOFS_SFTP_HOST}|" .env
sed -i "s|^FLOODPROOFS_SFTP_USER=.*|FLOODPROOFS_SFTP_USER=${FLOODPROOFS_SFTP_USER}|" .env
sed -i "s|^FLOODPROOFS_SFTP_PASSWORD=.*|FLOODPROOFS_SFTP_PASSWORD=${FLOODPROOFS_SFTP_PASSWORD}|" .env
sed -i "s|^WRF_FTP_HOST=.*|WRF_FTP_HOST=${WRF_FTP_HOST}|" .env
sed -i "s|^WRF_FTP_USER=.*|WRF_FTP_USER=${WRF_FTP_USER}|" .env
sed -i "s|^WRF_FTP_PASSWORD=.*|WRF_FTP_PASSWORD=${WRF_FTP_PASSWORD}|" .env
sed -i "s|^FLOODS_API_KEY=.*|FLOODS_API_KEY=${FLOODS_API_KEY}|" .env
sed -i "s|^SMTP_EMAIL_HOST=.*|SMTP_EMAIL_HOST=${SMTP_EMAIL_HOST}|" .env
sed -i "s|^SMTP_EMAIL_HOST_USER=.*|SMTP_EMAIL_HOST_USER=${SMTP_EMAIL_HOST_USER}|" .env
sed -i "s|^SMTP_EMAIL_HOST_PASSWORD=.*|SMTP_EMAIL_HOST_PASSWORD=${SMTP_EMAIL_HOST_PASSWORD}|" .env
sed -i "s|^CMS_DEBUG=.*|CMS_DEBUG=False|" .env
# If no services to deploy, just update repo and exit
if [ -z "${DEPLOY_SERVICES}" ]; then
echo "No services to deploy — repo updated, exiting"
exit 0
fi
# Deploy: pull by SHA, retag as latest, restart
chmod +x scripts/deploy.sh
./scripts/deploy.sh ${DEPLOY_SERVICES}