Skip to content

chore: week40 (#10866) #3401

chore: week40 (#10866)

chore: week40 (#10866) #3401

name: Container Image Deployment CI
on:
push:
branches:
- main
- deploy-*
tags:
- v*.*.*
# Note on secrets used for connection
# SSH_PRIVATE_KEY is the private key (shared between VM and host)
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
env:
- ${{ startsWith(github.ref, 'refs/tags/v') && 'off-org' || 'off-net' }}
environment: ${{ matrix.env }}
concurrency: ${{ matrix.env }}
steps:
# Put here non secret variables that change according to environment
- name: Set various variable for staging (net) deployment
if: matrix.env == 'off-net'
run: |
# direct container access
echo "REDIS_URL=redis:6379" >> $GITHUB_ENV
echo "RATE_LIMITER_BLOCKING_ENABLED=0" >> $GITHUB_ENV
echo "MONGODB_HOST=10.1.0.200" >> $GITHUB_ENV
echo "ROBOTOFF_URL=https://robotoff.openfoodfacts.net" >> $GITHUB_ENV
echo "QUERY_URL=http://10.1.0.200:5511" >> $GITHUB_ENV
echo "PRODUCT_OPENER_DOMAIN=openfoodfacts.net" >> $GITHUB_ENV
echo "PRODUCT_OPENER_FLAVOR=openfoodfacts" >> $GITHUB_ENV
echo "PRODUCT_OPENER_FLAVOR_SHORT=off" >> $GITHUB_ENV
echo "POSTGRES_USER=postgres" >> $GITHUB_ENV
echo "CROWDIN_PROJECT_IDENTIFIER=openfoodfacts" >> $GITHUB_ENV
echo "GEOLITE2_PATH=/usr/local/share/GeoLite2-Country/GeoLite2-Country.mmdb" >> $GITHUB_ENV
echo "INFLUXDB_HOST=10.1.0.200" >> $GITHUB_ENV
echo "FACETS_KP_URL=https://facets-kp.openfoodfacts.net/render-to-html" >> $GITHUB_ENV
# deploy target
echo "SSH_HOST=10.1.0.200" >> $GITHUB_ENV
echo "SSH_PROXY_HOST=ovh1.openfoodfacts.org" >> $GITHUB_ENV
echo "SSH_USERNAME=off" >> $GITHUB_ENV
- name: Set various variable for prod (org) deployment
if: matrix.env == 'off-org'
run: |
# FIXME: we do not deploy to prod currently
false
- name: Check for an eventual NO_DEPLOY file to put deployment on hold
uses: appleboy/ssh-action@master
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
if [[ -f /home/off/${{ matrix.env }}/NO_DEPLOY ]]
then
>&2 echo "A NO_DEPLOY file exists (in /home/off/${{ matrix.env }}), failing !"
exit 1
fi
- name: Wait for frontend container build workflow
uses: tomchv/wait-my-workflow@v1.1.0
id: wait-build
with:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: build (frontend)
ref: ${{ github.event.pull_request.head.sha || github.sha }}
intervalSeconds: 10
timeoutSeconds: 600 # 10m
- name: Wait for backend container build workflow
uses: tomchv/wait-my-workflow@v1.1.0
id: wait-build2
with:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: build (backend)
ref: ${{ github.event.pull_request.head.sha || github.sha }}
intervalSeconds: 10
timeoutSeconds: 600 # 10m
- name: Do something if build isn't launch
if: steps.wait-build.outputs.conclusion == 'does not exist' || steps.wait-build2.outputs.conclusion == 'does not exist'
run: echo job does not exist && true
- name: Do something if build fail
if: steps.wait-build.outputs.conclusion == 'failure' || steps.wait-build2.outputs.conclusion == 'failure'
run: echo fail && false # fail if build fail
- name: Do something if build timeout
if: steps.wait-build.outputs.conclusion == 'timed_out' || steps.wait-build2.outputs.conclusion == 'timed_out'
run: echo Timeout && false # fail if build time out
- name: Checkout git repository
uses: appleboy/ssh-action@master
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
script_stop: false
script: |
# while we would use shallow clone on a normal repo,
# comparison is too costly for this project and takes too much time, so we use a regular clone
# Clone Git repository if not already there
[ ! -d '${{ matrix.env }}' ] && git clone https://github.com/${{ github.repository }} ${{ matrix.env }} 2>&1
# Go to repository directory
cd ${{ matrix.env }}
# Fetch newest commits (in case it wasn't freshly cloned)
# here again we use full depth instead of usual --depth 1, because of perf problems
git fetch
# Checkout current commit SHA
git checkout -qf ${{ github.sha }}
- name: Set environment variables
uses: appleboy/ssh-action@master
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
script_stop: false
script: |
# Go to repository directory
cd ${{ matrix.env }}
# init .env
echo "# Env file generated by container-deploy action"> .env
# Set Docker Compose variables
echo "DOCKER_CLIENT_TIMEOUT=360" >> .env
echo "COMPOSE_HTTP_TIMEOUT=360" >> .env
echo "COMPOSE_PROJECT_NAME=po" >> .env
echo "COMPOSE_PATH_SEPARATOR=;" >> .env
echo "COMPOSE_FILE=docker-compose.yml;docker/prod.yml;docker/geolite2.yml;docker/monitor.yml" >> .env
echo "COMPOSE_PROFILES=off" >> .env
echo "PRODUCT_OPENER_EXPOSE=" >> .env
echo "ADMIN_EXPOSE=" >> .env
# Set App variables
echo "TAG=sha-${{ github.sha }}" >> .env
echo "WEB_RESOURCES_PATH=../off-web-net/" >> .env
echo "PRODUCERS_PLATFORM=0" >> .env
echo "PRODUCT_OPENER_PORT=80" >> .env
echo "PRODUCT_OPENER_DOMAIN=${{ env.PRODUCT_OPENER_DOMAIN }}" >> .env
echo "PRODUCT_OPENER_FLAVOR=${{ env.PRODUCT_OPENER_FLAVOR }}" >> .env
echo "PRODUCT_OPENER_FLAVOR_SHORT=${{ env.PRODUCT_OPENER_FLAVOR_SHORT }}" >> .env
echo "MINION_MODE=production" >> .env
echo "MINION_QUEUE=${{ env.PRODUCT_OPENER_DOMAIN }}" >> .env
echo "MONGODB_HOST=${{ env.MONGODB_HOST }}" >> .env
echo "POSTGRES_USER=${{ env.POSTGRES_USER }}" >> .env
echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env
echo "ROBOTOFF_URL=${{ env.ROBOTOFF_URL }}" >> .env
echo "QUERY_URL=${{ env.QUERY_URL }}" >> .env
echo "REDIS_URL=${{ env.REDIS_URL }}" >> .env
echo "FACETS_KP_URL=${{ env.FACETS_KP_URL }}" >> .env
echo "GOOGLE_CLOUD_VISION_API_KEY=${{ secrets.GOOGLE_CLOUD_VISION_API_KEY }}" >> .env
echo "CROWDIN_PROJECT_IDENTIFIER=${{ env.CROWDIN_PROJECT_IDENTIFIER }}" >> .env
echo "CROWDIN_PROJECT_KEY=${{ secrets.CROWDIN_PROJECT_KEY }}" >> .env
echo "GEOLITE2_PATH=${{ env.GEOLITE2_PATH }}" >> .env
echo "GEOLITE2_LICENSE_KEY=${{ secrets.GEOLITE2_LICENSE_KEY }}" >> .env
echo "GEOLITE2_ACCOUNT_ID=${{ secrets.GEOLITE2_ACCOUNT_ID }}" >> .env
echo "INFLUXDB_HOST=${{ env.INFLUXDB_HOST }}" >> .env
echo "LOG_LEVEL_ROOT=ERROR" >> .env
echo "LOG_LEVEL_MONGODB=ERROR" >> .env
echo "LOG_LEVEL_RATE_LIMITER=INFO" >> .env
echo "BUILD_CACHE_REPO=openfoodfacts/openfoodfacts-build-cache" >> .env
# Override domain name in nginx.conf
sed -i.bak "s/productopener.localhost/${{ env.PRODUCT_OPENER_DOMAIN }}/g" ./conf/nginx-docker/nginx.conf
- name: Create Docker volumes and network
uses: appleboy/ssh-action@master
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
script_stop: false
script: |
cd ${{ matrix.env }} && \
make create_external_volumes && \
make create_external_networks
- name: wait for docker images to be available
# we sometimes have the images not ready immediately after build, so we prefer to wait for it
uses: appleboy/ssh-action@master
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
command_timeout: 25m
script_stop: false
script: |
waited=0
for IMAGE_TYPE in frontend backend
do
image_name=ghcr.io/${{ github.repository }}/$IMAGE_TYPE:sha-${{ github.sha }}
# pull image, eventually waiting for it to be available in ghcr.io
while ! (docker pull $image_name); do
sleep 30
waited=$((waited+30))
# timeout after 6 minutes (gloablly)
if [[ $waited -gt 360 ]]; then
echo "Timeout waiting for image $image_name"
# Abort the job
exit 1
fi
done
done
- name: initialize the backend
uses: appleboy/ssh-action@master
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
command_timeout: 25m
script_stop: false
script: |
cd ${{ matrix.env }} && \
make init_backend
- name: Start services
uses: appleboy/ssh-action@master
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
command_timeout: 15m
script_stop: false
script: |
cd ${{ matrix.env }} && \
make hdown && \
make build_lang && \
make prod_up
- name: Check services are up
uses: appleboy/ssh-action@master
if: ${{ always() }}
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
script_stop: false
script: |
cd ${{ matrix.env }} && \
make livecheck
- name: Cleanup obsolete Docker objects
uses: appleboy/ssh-action@master
if: ${{ always() }}
with:
host: ${{ env.SSH_HOST }}
username: ${{ env.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
proxy_host: ${{ env.SSH_PROXY_HOST }}
proxy_username: ${{ env.SSH_USERNAME }}
proxy_key: ${{ secrets.SSH_PRIVATE_KEY }}
script_stop: false
script: |
cd ${{ matrix.env }} && \
make prune
- uses: basos9/grafana-annotation-action@v1.0.3
if: ${{ always() }}
with:
apiHost: https://grafana.openfoodfacts.org
apiToken: ${{ secrets.GRAFANA_API_TOKEN }}
text: <a href="https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}">Deployment ${{ steps.livecheck.outcome }} on ${{ matrix.env }}</a>
tags: type:deployment,origin:github,status:${{ steps.livecheck.outcome }},repo:${{ github.repository }},sha:${{ github.sha }},app:robotoff,env:${{ matrix.env }}