diff --git a/docker-compose.yml b/docker-compose.yml index c31e71404..b2316da09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,11 +23,13 @@ services: build: context: . dockerfile: docker/Dockerfile.rails - command: bundle exec thin start -p 3000 --ssl --ssl-key-file dev_server_keys/server.key --ssl-cert-file dev_server_keys/server.crt + command: bash docker/rails_entry.sh + environment: + BUNDLE_PATH: "/bundle" ports: - "3000:3000" volumes: - - rails-app-sync:/app/app:nocopy + - rails-app-sync:/project_root:nocopy depends_on: - mysql - jetty diff --git a/docker-sync.yml b/docker-sync.yml index 1b546fbbe..197e4ef86 100644 --- a/docker-sync.yml +++ b/docker-sync.yml @@ -4,6 +4,5 @@ options: verbose: true syncs: rails-app-sync: # tip: add -sync and you keep consistent names as a convention - src: './app' - # sync_strategy: 'native_osx' # not needed, this is the default now - sync_excludes: ['ignored_folder', '.ignored_dot_folder'] + src: '.' + sync_excludes: ['.git', 'tmp', 'jetty', 'vendor/bundle'] diff --git a/docker/Dockerfile.rails b/docker/Dockerfile.rails index 621a33278..62d1e2e64 100644 --- a/docker/Dockerfile.rails +++ b/docker/Dockerfile.rails @@ -1,9 +1,17 @@ FROM ruby:2.1.10 RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs -RUN mkdir /app -WORKDIR /app -COPY . /app +# Put the installed gems outside of project_root so that the sync volume won't interfere +RUN mkdir /bundle +COPY Gemfile /bundle +COPY Gemfile.lock /bundle +WORKDIR /bundle +RUN bundle install --without headless --path /bundle -RUN bundle install --without headless +RUN mkdir /project_root +WORKDIR /project_root +COPY . /project_root + +# Gemfile.lock may have changed after bundling, copy it back into the project_root +RUN cp /bundle/Gemfile.lock /project_root/ diff --git a/docker/rails_entry.sh b/docker/rails_entry.sh new file mode 100755 index 000000000..039fa9b23 --- /dev/null +++ b/docker/rails_entry.sh @@ -0,0 +1,7 @@ +# All the things that will execute when starting the rails service +# Copy the generated lock file back into the project root. This is to sync this change back to the +# application after the container has mounted the sync volume +cp /bundle/Gemfile.lock ./ +bash docker/wait-for-it.sh mysql:3306 +bundle exec rake db:schema:load +bundle exec thin start -p 3000 --ssl --ssl-key-file dev_server_keys/server.key --ssl-cert-file dev_server_keys/server.crt diff --git a/docker/wait-for-it.sh b/docker/wait-for-it.sh new file mode 100755 index 000000000..bbe404324 --- /dev/null +++ b/docker/wait-for-it.sh @@ -0,0 +1,177 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +cmdname=$(basename $0) + +echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $TIMEOUT -gt 0 ]]; then + echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT" + else + echoerr "$cmdname: waiting for $HOST:$PORT without a timeout" + fi + start_ts=$(date +%s) + while : + do + if [[ $ISBUSY -eq 1 ]]; then + nc -z $HOST $PORT + result=$? + else + (echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1 + result=$? + fi + if [[ $result -eq 0 ]]; then + end_ts=$(date +%s) + echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds" + break + fi + sleep 1 + done + return $result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $QUIET -eq 1 ]]; then + timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + else + timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + fi + PID=$! + trap "kill -INT -$PID" INT + wait $PID + RESULT=$? + if [[ $RESULT -ne 0 ]]; then + echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT" + fi + return $RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + hostport=(${1//:/ }) + HOST=${hostport[0]} + PORT=${hostport[1]} + shift 1 + ;; + --child) + CHILD=1 + shift 1 + ;; + -q | --quiet) + QUIET=1 + shift 1 + ;; + -s | --strict) + STRICT=1 + shift 1 + ;; + -h) + HOST="$2" + if [[ $HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + HOST="${1#*=}" + shift 1 + ;; + -p) + PORT="$2" + if [[ $PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + PORT="${1#*=}" + shift 1 + ;; + -t) + TIMEOUT="$2" + if [[ $TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$HOST" == "" || "$PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +TIMEOUT=${TIMEOUT:-15} +STRICT=${STRICT:-0} +CHILD=${CHILD:-0} +QUIET=${QUIET:-0} + +# check to see if timeout is from busybox? +# check to see if timeout is from busybox? +TIMEOUT_PATH=$(realpath $(which timeout)) +if [[ $TIMEOUT_PATH =~ "busybox" ]]; then + ISBUSY=1 + BUSYTIMEFLAG="-t" +else + ISBUSY=0 + BUSYTIMEFLAG="" +fi + +if [[ $CHILD -gt 0 ]]; then + wait_for + RESULT=$? + exit $RESULT +else + if [[ $TIMEOUT -gt 0 ]]; then + wait_for_wrapper + RESULT=$? + else + wait_for + RESULT=$? + fi +fi + +if [[ $CLI != "" ]]; then + if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then + echoerr "$cmdname: strict mode, refusing to execute subprocess" + exit $RESULT + fi + exec "${CLI[@]}" +else + exit $RESULT +fi