#!/bin/bash
 

Fail early upon error

set -eu

info () {
    echo "I: $@"
}
 

This file will exist if we’ve initialized postgres

stamp=/var/lib/postgresql/11/main/initialized.stamp
 

Ensure the user starting the container has provided a password

if [ -z "$POSTGRES_PASSWORD" ]
then
    echo "Please export \${POSTGRES_PASSWORD}"
    exit 1
fi
 

The RUNESTONE_HOST will be used by pavement.py files of runestone books to set the correct host for so that Browser and Server agree on a CORS compliant host for API calls

if [ -z "$RUNESTONE_HOST" ]; then
    echo "Please export \${RUNESTONE_HOST} set to the hostname"
    exit 1
fi
 

Initialize the database

if [ ! -f "$stamp" ]; then

    info "Install rsmanage local module"
    pip install -e ${RUNESTONE_PATH}/rsmanage

    info "Creating auth key"
    mkdir -p ${RUNESTONE_PATH}/private
    echo "sha512:16492eda-ba33-48d4-8748-98d9bbdf8d33" > ${RUNESTONE_PATH}/private/auth.key

    info "Creating pgpass file"
    echo "db:5432:*:$POSTGRES_USER:$POSTGRES_PASSWORD" > /root/.pgpass
    chmod 600 /root/.pgpass
 

add a new setting so that institutions can run using a base book like thinkcspy as their course. On Runestone.academy we don’t let anyone be an instructor for the base courses because they are open to anyone. This makes for a much less complicated deployment strategy for an institution that just wants to run their own server and use one or two books.

    if [ ! -f "${RUNESTONE_PATH}/models/1.py" ]; then
        touch "${RUNESTONE_PATH}/models/1.py"
        echo "settings.docker_institution_mode = True" >> "${RUNESTONE_PATH}/models/1.py"
        echo "settings.jobe_key = ''" >> "${RUNESTONE_PATH}/models/1.py"
        echo "settings.jobe_server = 'http://jobe'" >> "${RUNESTONE_PATH}/models/1.py"
    fi

    set +e
    if [[ -z "${CERTBOT_EMAIL}" ]]; then
        echo "CERTBOT_EMAIL not set will not attempt certbot setup -- NO https!!"
    else
        certbot -n  --agree-tos --email "${CERTBOT_EMAIL}" --nginx --redirect -d "${RUNESTONE_HOST}"
        echo "You should be good for https"
    fi
    set -e

    touch "${stamp}"
else
    info "Already initialized"
fi

RETRIES=10
set +e
until psql $DBURL -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do
  echo "Waiting for postgres server, $((RETRIES--)) remaining attempts..."
  sleep 2
done

info "Checking the State of Database and Migration Info"
rsmanage env --checkdb
dbstate="$?"
info "Got result of $dbstate"
set -e
 

since this is a dev environment a rebuild of the container does not necessarily mean that the book you want will be totally clean, and so a build of an already built book in a fresh container will result in a database that is missing the questions. setting buildargs to –all when we have a clean db will ensure that does not happen

 
buildargs=""

case $dbstate in
    0)
        info "Initializing DB and databases"
        rsmanage initdb
        buildargs="--all"
        ;;
    1)
        info "Removing databases folder and initializing"
        rsmanage initdb --reset --force
        buildargs="--all"
        ;;
    2)
        info "Warning -- Database initialized but missing databases/ Trying a fake migration"
        rsmanage migrate --fake
        ;;
    3)
        info "All is good, no initialization needed"
        ;;
    *)
        info "Unexpected result from checkdb"
        exit 1

esac

info "Updating file ownership"
mkdir -p /srv/web2py/logs
touch /srv/web2py/logs/uwsgi.logs
chown -R www-data /srv/web2py
mkdir -p /run/uwsgi
mkdir -p ${RUNESTONE_PATH}/databases
chown www-data ${RUNESTONE_PATH}/databases
chown -R www-data /run/uwsgi
 

If you want to do development on the components as well, then install them in dev mode. You should also make a docker-compose.override.yml that looks like version: “3”

services:
runestone:
volumes:
  • ../RunestoneComponents:/srv/RunestoneComponents

 
if [ -f /srv/RunestoneComponents/README.rst ]; then
    info "Installing Development Version of Runestone"
    pip install --upgrade -e /srv/RunestoneComponents
    info "Make sure you execute the command npm run build to update runestone.js"
fi
runestone --version
 

For development, make all files group-writeable.

if [ $WEB2PY_CONFIG == "development" ]; then
    chmod -R g+w ${RUNESTONE_PATH}
fi
 
 

Setup instructors, if the file exists

if [ -f "${RUNESTONE_PATH}/configs/instructors.csv" -a "${RUNESTONE_PATH}/configs/instructors.csv" -nt iadd.stamp ]; then
    info "Setting up instructors"
    rsmanage inituser --fromfile ${RUNESTONE_PATH}/configs/instructors.csv
    cut -d, -f1,6 ${RUNESTONE_PATH}/configs/instructors.csv \
    | tr ',' ' ' \
    | while read n c ; do
        rsmanage addinstructor  --username $n --course $c  || echo "unable to add instructor"
    done
    touch iadd.stamp
fi
 

Setup students, again if the file exists

if [ -f "${RUNESTONE_PATH}/configs/students.csv" -a "${RUNESTONE_PATH}/configs/students.csv" -nt sadd.stamp ]; then
    info "Setting up students"
    rsmanage inituser --fromfile ${RUNESTONE_PATH}/configs/students.csv
    info "Students were provided -- disabling signup!"

Disable signup

    echo -e "\nauth.settings.actions_disabled.append('register')" >> $WEB2PY_PATH/applications/runestone/models/db.py
    touch sadd.stamp
fi
 

Uncomment for debugging /bin/bash

 

Run the beast

info "Starting the server"
cd "$WEB2PY_PATH"
 

To just run the development server Do this: python web2py.py –ip=0.0.0.0 –port=8080 –password=”${POSTGRES_PASSWORD}” -K runestone –nogui -X &

 

To start in a mode more consistent with deployment Do this:

info "starting nginx"
service nginx start

info "starting uwsgi"
/usr/local/bin/uwsgi --ini /etc/uwsgi/sites/runestone.ini &



## Go through all books and build
if [ $BUILD_BOOKS == 'yes' ]; then
  info "Building & Deploying books"
  cd "${BOOKS_PATH}"
  /bin/ls | while read book; do
      (
          rsmanage courseinfo --name $book
          if [ $? -eq 0 ]; then
            cd $book;
            if [ ! -f NOBUILD ]; then
                if [ -f requirements.txt ]; then
                    pip install -r requirements.txt
                fi
                runestone build $buildargs deploy
            else
                info "skipping $book due to NOBUILD file"
            fi
          else
            info "There is no database info for $book -- skipping"
            info "You should add a new book to the database before building."
          fi
      );
  done
fi

tail -F ${WEB2PY_PATH}/logs/uwsgi.log