From 3104c480c5f55c42f5e9c265a965cb4c652541a8 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Tue, 19 Mar 2019 06:31:39 +0800 Subject: [PATCH] Implement docker-run.sh script + docker container build (#1844) [ci skip] --- .dockerignore | 68 +++++- .gitignore | 5 +- Dockerfile | 249 +++++++++++++++++++-- docker-entrypoint.sh | 80 +++++++ docker-run.sh | 512 +++++++++++++++++++++++++++++++++++++++++++ dockerhub-readme.md | 35 +++ 6 files changed, 924 insertions(+), 25 deletions(-) create mode 100755 docker-entrypoint.sh create mode 100755 docker-run.sh create mode 100644 dockerhub-readme.md diff --git a/.dockerignore b/.dockerignore index 3fa9d2c28cd4b..8eaa76600ef9b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,65 @@ .dockerignore - .git - logs/ - tmp/ - node_modules/ +.git +.gitignore +logs/ +tmp/ +node_modules/ + +################################## +# +# rest is COPIED from .gitignore +# +################################## + +# Ignore bundler config. +/.bundle +vendor/bundle + +# Ignore all logfiles and tempfiles. +/log/* +!/log/.keep +/tmp +.DS_Store +.swp +.approvals +.torus.json +coverage +/tags + +# Ignore public uploads +/public/uploads/* +/public/c/* +/public/i/* +/public/assets/* + +# Ignore node_modules +node_modules/ + +# Generated js bundles +/app/assets/javascripts/generated/* +latest.dump +.byebug_history +.gitdocs_build/ + +# Ignore application configuration +/config/application.yml +/public/packs +/public/packs-test +/node_modules +config/database.yml + +# Ignore storybook static site generation +storybook-static/ +yarn-error.log + +# Ignore package-lock.json because we use yarn +package-lock.json + +#Jetbrains Tools +.idea/ + +#sitemap +/public/sitemap.xml.gz + +# Development Docker storage location +_docker-storage/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index df2840a65ed98..d67b5c4207b41 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,7 @@ package-lock.json .idea/ #sitemap -/public/sitemap.xml.gz \ No newline at end of file +/public/sitemap.xml.gz + +# Development Docker storage location +_docker-storage/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a95bcafa89c94..20c8fc77942f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,31 +1,240 @@ -FROM ruby:2.6.1 +##################################################### +# +# Alpine container with +# (this is used in DEV mode) +# +# + ruby:2.6.1 +# + node:8.15.0 +# + yarn:1.12.3 +# +##################################################### +FROM node:8.15.1-alpine AS alpine-ruby-node -# Make nodejs and yarn as dependencies -RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - -RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list +#------------------------------------------------------------------------------------------ +# +# Ruby installation, taken from the official ruby alpine dockerfile +# see : https://github.com/docker-library/ruby/blob/96fc06fb331a20ba823ecc11563a99d1eb94203f/2.6/alpine3.9/Dockerfile +# +#------------------------------------------------------------------------------------------ -# Install dependencies and perform clean-up -RUN apt-get update -qq && apt-get install -y \ - build-essential \ - nodejs \ - yarn \ - && apt-get -q clean \ - && rm -rf /var/lib/apt/lists +RUN apk add --no-cache \ + gmp-dev +# skip installing gem documentation +RUN mkdir -p /usr/local/etc \ + && { \ + echo 'install: --no-document'; \ + echo 'update: --no-document'; \ + } >> /usr/local/etc/gemrc + +ENV RUBY_MAJOR 2.6 +ENV RUBY_VERSION 2.6.1 +ENV RUBY_DOWNLOAD_SHA256 47b629808e9fd44ce1f760cdf3ed14875fc9b19d4f334e82e2cf25cb2898f2f2 +ENV RUBYGEMS_VERSION 3.0.3 + +# some of ruby's build scripts are written in ruby +# we purge system ruby later to make sure our final image uses what we just built +# readline-dev vs libedit-dev: https://bugs.ruby-lang.org/issues/11869 and https://github.com/docker-library/ruby/issues/75 +RUN set -ex \ + \ + && apk add --no-cache --virtual .ruby-builddeps \ + autoconf \ + bison \ + bzip2 \ + bzip2-dev \ + ca-certificates \ + coreutils \ + dpkg-dev dpkg \ + gcc \ + gdbm-dev \ + glib-dev \ + libc-dev \ + libffi-dev \ + libxml2-dev \ + libxslt-dev \ + linux-headers \ + make \ + ncurses-dev \ + openssl \ + openssl-dev \ + procps \ + readline-dev \ + ruby \ + tar \ + xz \ + yaml-dev \ + zlib-dev \ + \ + && wget -O ruby.tar.xz "https://cache.ruby-lang.org/pub/ruby/${RUBY_MAJOR%-rc}/ruby-$RUBY_VERSION.tar.xz" \ + && echo "$RUBY_DOWNLOAD_SHA256 *ruby.tar.xz" | sha256sum -c - \ + \ + && mkdir -p /usr/src/ruby \ + && tar -xJf ruby.tar.xz -C /usr/src/ruby --strip-components=1 \ + && rm ruby.tar.xz \ + \ + && cd /usr/src/ruby \ + \ +# https://github.com/docker-library/ruby/issues/196 +# https://bugs.ruby-lang.org/issues/14387#note-13 (patch source) +# https://bugs.ruby-lang.org/issues/14387#note-16 ("Therefore ncopa's patch looks good for me in general." -- only breaks glibc which doesn't matter here) + && wget -O 'thread-stack-fix.patch' 'https://bugs.ruby-lang.org/attachments/download/7081/0001-thread_pthread.c-make-get_main_stack-portable-on-lin.patch' \ + && echo '3ab628a51d92fdf0d2b5835e93564857aea73e0c1de00313864a94a6255cb645 *thread-stack-fix.patch' | sha256sum -c - \ + && patch -p1 -i thread-stack-fix.patch \ + && rm thread-stack-fix.patch \ + \ +# hack in "ENABLE_PATH_CHECK" disabling to suppress: +# warning: Insecure world writable dir + && { \ + echo '#define ENABLE_PATH_CHECK 0'; \ + echo; \ + cat file.c; \ + } > file.c.new \ + && mv file.c.new file.c \ + \ + && autoconf \ + && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ +# the configure script does not detect isnan/isinf as macros + && export ac_cv_func_isnan=yes ac_cv_func_isinf=yes \ + && ./configure \ + --build="$gnuArch" \ + --disable-install-doc \ + --enable-shared \ + && make -j "$(nproc)" \ + && make install \ + \ + && runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )" \ + && apk add --no-network --virtual .ruby-rundeps $runDeps \ + bzip2 \ + ca-certificates \ + libffi-dev \ + procps \ + yaml-dev \ + zlib-dev \ + && apk del --no-network .ruby-builddeps \ + && cd / \ + && rm -r /usr/src/ruby \ +# make sure bundled "rubygems" is older than RUBYGEMS_VERSION (https://github.com/docker-library/ruby/issues/246) + && ruby -e 'exit(Gem::Version.create(ENV["RUBYGEMS_VERSION"]) > Gem::Version.create(Gem::VERSION))' \ + && gem update --system "$RUBYGEMS_VERSION" && rm -r /root/.gem/ \ +# rough smoke test + && ruby --version && gem --version && bundle --version + +# install things globally, for great justice +# and don't create ".bundle" in all our apps +ENV GEM_HOME /usr/local/bundle +ENV BUNDLE_PATH="$GEM_HOME" \ + BUNDLE_SILENCE_ROOT_WARNING=1 \ + BUNDLE_APP_CONFIG="$GEM_HOME" +# path recommendation: https://github.com/bundler/bundler/pull/6469#issuecomment-383235438 +ENV PATH $GEM_HOME/bin:$BUNDLE_PATH/gems/bin:$PATH +# adjust permissions of a few directories for running "gem install" as an arbitrary user +RUN mkdir -p "$GEM_HOME" && chmod 777 "$GEM_HOME" +# (BUNDLE_PATH = GEM_HOME, no need to mkdir/chown both) + +#------------------------------------------------------------------------------------------ +# +# End of ruby installation +# +#------------------------------------------------------------------------------------------ + +# Install alpine eqivalent for "build-essential" +# +# and other dependencies required by dev.to various +# ruby dependencies (postgresql-dev, tzdata) +RUN apk add --no-cache alpine-sdk postgresql-dev tzdata + +# Im installing bash, as im a bash addict (not that great with sh) +RUN apk add bash + +# Lets setup the rails directory +# (@TODO - consider a production version?) WORKDIR /usr/src/app ENV RAILS_ENV development -# Installing Ruby dependencies -COPY Gemfile* ./ +##################################################### +# +# Lets prepare the dev.to source code files +# WITHOUT docker related files +# +# This allow us to modify the docker +# entrypoint / run file without recompiling +# the entire application +# (especially when creating this buidl script =| ) +# +# (@TODO - improve and review ignore to blacklist unneded items) +# +##################################################### + +# +# Prepare the source code and remove any uneeded files +# +FROM alpine-ruby-node AS source-code-repo + +# The workdir +WORKDIR /usr/src/app +# Copy source code +COPY ./ /usr/src/app/ +# remove docker related files +RUN rm Dockerfile && rm docker-* + +# +# Does the source code build +# +FROM alpine-ruby-node AS source-code-build + +# Copy over files +COPY --from=source-code-repo /usr/src/app/ /usr/src/app/ + +# Run the various installer RUN gem install bundler RUN bundle install --jobs 20 --retry 5 - -# Install JavaScript dependencies -COPY yarn.lock ./ -ENV YARN_INTEGRITY_ENABLED "false" RUN yarn install && yarn check --integrity -ENTRYPOINT ["bundle", "exec"] +##################################################### +# +# Lets build the DEMO dev.to image +# +##################################################### +FROM alpine-ruby-node + +# Copy over the application code (without docker related files) +COPY --from=source-code-build /usr/src/app/ /usr/src/app/ + +# Copy over docker related files +COPY Dockerfile [(docker-)]* /usr/src/app/ + +# +# Execution environment variables +# + +# timeout extension requried to ensure +# system work properly on first time load +ENV RACK_TIMEOUT_WAIT_TIMEOUT=10000 \ + RACK_TIMEOUT_SERVICE_TIMEOUT=10000 \ + STATEMENT_TIMEOUT=10000 + +# Run mode configuration between dev / demo +# for entrypoint script behaviour +ENV RUN_MODE="demo" + +# Database URL configuration - with user/pass +ENV DATABASE_URL="postgresql://devto:devto@db:5432/PracticalDeveloper_development" + +# DB setup / migrate script triggers on boot +ENV DB_SETUP="true" \ + DB_MIGRATE="true" + +# +# Lets setup the public uploads folder volume +# +RUN mkdir -p /usr/src/app/public/uploads +VOLUME /usr/src/app/public/uploads -CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"] +# Entrypoint and command to start the server +ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"] +CMD [] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000000000..5328f014a7a42 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# +# Lets setup the alias file +# @TODO - add as scripts instead within /bin? - this will help auto fill? +# +echo "" > ~/.bashrc +echo "alias devto-setup='cd /usr/src/app/ && gem install bundler && bundle install --jobs 20 --retry 5 && yarn install && yarn check --integrity && bin/setup'" >> ~/.bashrc +echo "alias devto-migrate='cd /usr/src/app/ && bin/rails db:migrate'" >> ~/.bashrc +echo "alias devto-start='cd /usr/src/app/ && bundle exec rails server -b 0.0.0.0 -p 3000'" >> ~/.bashrc + +# +# Lets ensure we are in the correct workspace +# +cd /usr/src/app/ + +# +# Lets handle "DEV" RUN_MODE +# +if [[ "$RUN_MODE" = "DEV" ]] +then + echo ">---" + echo "> [dev.to/docker-entrypoint.sh] DEV mode" + echo "> " + echo "> Welcome to the dev.to, DEVELOPMENT container, for convinence your repository" + echo "> should be mounted onto '/usr/src/app/', and port 3000 should be forwarded to your host machine" + echo "> " + echo "> In addition the following alias commands has been preconfigured to get you up and running quickly" + echo "> " + echo "> devto-setup : Does the gem/yarn dependency installation, along with database setup" + echo "> devto-migrate : Calls the database migration script" + echo "> devto-start : Start the rails application server on port 3000" + echo "> " + echo "> Finally to exit this container bash terminal (and stop the container), use the command 'exit'" + echo ">---" + + # Lets startup bash for the user to interact with + /bin/bash + exit $?; +fi + +# +# Lets handle "DEMO" RUN_MODE +# +echo ">---" +echo "> [dev.to/docker-entrypoint.sh] DEMO mode" +echo "> " +echo "> Time to rock & roll" +echo ">---" + +# +# DB setup +# note this will fail (intentionally), if DB was previously setup +# +if [[ "$DB_SETUP" == "true" ]] +then + echo ">---" + echo "> [dev.to/docker-entrypoint.sh] Performing DB_SETUP : you can skip this step by setting DB_SETUP=false" + echo ">---" + bin/setup +fi + +# +# DB migration script +# +if [[ "$DB_MIGRATE" == "true" ]] +then + echo ">---" + echo "> [dev.to/docker-entrypoint.sh] Performing DB_MIGRATE : you can skip this step by setting DB_MIGRATE=false" + echo ">---" + bin/rails db:migrate +fi + +# +# Execute rails server on port 3000 +# +echo ">---" +echo "> [dev.to/docker-entrypoint.sh] Starting the rails servers - whheee!" +echo ">---" +bundle exec rails server -b 0.0.0.0 -p 3000 diff --git a/docker-run.sh b/docker-run.sh new file mode 100755 index 0000000000000..7a424de30c432 --- /dev/null +++ b/docker-run.sh @@ -0,0 +1,512 @@ +#!/bin/bash + +########################################### +# +# Interactive mode script handling +# +########################################### + +if [ "$1" = "INTERACTIVE-DEMO" ] +then + # echo "#---" + # echo "# Starting up INTERACTIVE-DEMO mode" + # echo "#---" + + # Configure RUN_MODE as DEMO + RUN_MODE="DEMO" + + echo "|---" + echo "|" + echo "| Welcome to DEV.TO interactive docker demo setup guide." + echo "|" + echo "| For this container to work, we will need at minimum ALGOLIA API keys" + echo "| For logins to work, we will need either GITHUB or TWITTER API keys" + echo "|" + echo "| See ( https://docs.dev.to/get-api-keys-dev-env/ ) " + echo "| for instructions on how to get the various API keys " + echo "|" + echo "| Once you got your various API keys, please proceed to the next step" + echo "|" + echo "|---" + + echo "|---" + echo "| Setting up ALGOLIASEARCH keys (required)" + echo "|---" + echo -n "| Please indicate your ALGOLIASEARCH_APPLICATION_ID : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export ALGOLIASEARCH_APPLICATION_ID="$INPUT_KEY" + fi + + echo -n "| Please indicate your ALGOLIASEARCH_SEARCH_ONLY_KEY : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export ALGOLIASEARCH_SEARCH_ONLY_KEY="$INPUT_KEY" + fi + + echo -n "| Please indicate your ALGOLIASEARCH_API_KEY (aka admin key) : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export ALGOLIASEARCH_API_KEY="$INPUT_KEY" + fi + + echo "|---" + echo "| Setting up GITHUB keys" + echo "| (OPTIONAL, leave blank and press enter to skip)" + echo "|---" + echo -n "| Please indicate your GITHUB_KEY : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export GITHUB_KEY="$INPUT_KEY" + fi + + echo -n "| Please indicate your GITHUB_SECRET : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export GITHUB_SECRET="$INPUT_KEY" + fi + + echo -n "| Please indicate your GITHUB_TOKEN : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export GITHUB_TOKEN="$INPUT_KEY" + fi + + echo "|---" + echo "| Setting up TWITTER keys" + echo "| (OPTIONAL, leave blank and press enter to skip)" + echo "|---" + echo -n "| Please indicate your TWITTER_ACCESS_TOKEN : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export TWITTER_ACCESS_TOKEN="$INPUT_KEY" + fi + + echo -n "| Please indicate your TWITTER_ACCESS_TOKEN_SECRET : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export TWITTER_ACCESS_TOKEN_SECRET="$INPUT_KEY" + fi + + echo -n "| Please indicate your TWITTER_KEY : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export TWITTER_KEY="$INPUT_KEY" + fi + + echo -n "| Please indicate your TWITTER_SECRET : " + read INPUT_KEY + if [ ! -z "$INPUT_KEY" ] + then + export TWITTER_SECRET="$INPUT_KEY" + fi + +fi + +########################################### +# +# Script header guide +# +########################################### + +echo "#---" +echo "#" +echo "# This script will perform the following steps ... " +echo "#" +echo "# 1) Stop and remove any docker container with the name 'dev-to-postgres' and 'dev-to'" +echo "# 2) Reset any storage directories if RUN_MODE starts with 'RESET-'" +echo "# 3) Build the dev.to docker image, with the name of 'dev-to:dev' or 'dev-to:demo'" +echo "# 4) Deploy the postgres container, mounting '_docker-storage/postgres' with the name 'dev-to-postgres'" +echo "# 5) Deploy the dev-to container, with the name of 'dev-to-app', and sets up its port to 3000" +echo "#" +echo "# To run this script properly, execute with the following (inside the dev.to repository folder)..." +echo "# './docker-run.sh [RUN_MODE] [Additional docker envrionment arguments]'" +echo "#" +echo "# Alternatively to run this script in 'interactive mode' simply run" +echo "# './docker-run.sh INTERACTIVE-DEMO'" +echo "#" +echo "#---" +echo "#---" +echo "#" +echo "# RUN_MODE can either be the following" +echo "#" +echo "# - 'DEV' : Start up the container into bash, with a quick start guide" +echo "# - 'DEMO' : Start up the container, and run dev.to (requries ALGOLIA environment variables)" +echo "# - 'RESET-DEV' : Resets postgresql and upload data directory for a clean deployment, before running as DEV mode" +echo "# - 'RESET-DEMO' : Resets postgresql and upload data directory for a clean deployment, before running as DEMO mode" +echo "# - 'INTERACTIVE-DEMO' : Runs this script in 'interactive' mode to setup the 'DEMO'" +echo "#" +echo "# So for example to run a development container in bash its simply" +echo "# './docker-run.sh DEV'" +echo "#" +echo "# To run a simple demo, with some dummy data (replace with the actual keys)" +echo "# './docker-run.sh DEMO -e ALGOLIASEARCH_APPLICATION_ID= -e ALGOLIASEARCH_SEARCH_ONLY_KEY= -e ALGOLIASEARCH_API_KEY='" +echo "#" +echo "# Finally to run a working demo, you will need to provide either..." +echo "# './docker-run.sh .... -e GITHUB_KEY= -e GITHUB_SECRET= -e GITHUB_TOKEN=" +echo "#" +echo "# And / Or ..." +echo "# './docker-run.sh .... -e TWITTER_ACCESS_TOKEN= -e TWITTER_ACCESS_TOKEN_SECRET= -e TWITTER_KEY= -e TWITTER_SECRET=" +echo "#" +echo "# Note that all of this can also be configured via ENVIRONMENT variables prior to running the script" +echo "#" +echo "#---" + +########################################### +# +# Core script logic +# +########################################### + +# +# Arguments / Environment handling +# + +# Terminate without argument +if [ -z "$1" ] +then + # Invalid RUN_MODE + echo "#---" + echo "# [FATAL ERROR] Missing RUN_MODE argument (see example above)" + echo "#---" + exit 1 +fi + +# Initialize : docker-run.sh RUN_MODE +# also parses it from argument array +if [ -z "$RUN_MODE" ] +then + RUN_MODE="DEMO" +fi +# Argument processing +if [ "$1" = "DEMO" ] || [ "$1" = "DEV" ] || [ "$1" = "RESET-DEV" ] || [ "$1" = "RESET-DEMO" ] +then + # The mode is passed into the command line script, process it + RUN_MODE="$1" + # Process argument array without RUN_MODE + ARG_ARRAY_STR="${@:2}" +else + if [ "$1" = "INTERACTIVE-DEMO" ] + then + RUN_MODE="DEMO" + # Process argument array without RUN_MODE + ARG_ARRAY_STR="${@:2}" + else + # Process argument array with $0 + ARG_ARRAY_STR="${@:1}" + fi +fi +if [ "$RUN_MODE" = "DEMO" ] || [ "$RUN_MODE" = "DEV" ] || [ "$RUN_MODE" = "RESET-DEV" ] || [ "$RUN_MODE" = "RESET-DEMO" ] +then + # OK we validated run mode + RUN_MODE=$RUN_MODE +else + # Invalid RUN_MODE + echo "#---" + echo "# [FATAL ERROR] Invalid RUN_MODE : $RUN_MODE (see example above)" + echo "#---" + exit 2 +fi + +# Initialize : the currrent repository directory (and navigate to it) +if [ -z "$REPO_DIR" ] +then + REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +fi +cd "$REPO_DIR"; + +# Initialize : the general storage directory (used to magic bash the upload / postgres directory) +if [ -z "$STORAGE_DIR" ] +then + STORAGE_DIR="$REPO_DIR/_docker-storage" +fi + +# Initialize : Postgresql data directory +if [ -z "$POSTGRES_DIR" ] +then + POSTGRES_DIR="$STORAGE_DIR/postgresql-data/" +fi + +# Initialize : dev-to public upload data +if [ -z "$UPLOAD_DIR" ] +then + UPLOAD_DIR="$STORAGE_DIR/public-upload/" +fi + +echo "#---" +echo "# Ok, to start with - lets assume the following settings (provided or auto default)..." +echo "#" +echo "# RUN_MODE = $RUN_MODE" +echo "# REPO_DIR = $REPO_DIR" +echo "# STORAGE_DIR = $STORAGE_DIR" +echo "# UPLOAD_DIR = $UPLOAD_DIR" +echo "# POSTGRES_DIR = $POSTGRES_DIR" +echo "#" +echo "# PS : These settings can also be overwritten by ENVIRONMENT variables, extremely useful in a CI setup =)" +echo "#---" + +# +# ENV variables to support forwarding, and the compulsory list from bash script to docker (if detected) +# +ENV_FORWARDING_LIST=( + # ALGOLIASEARCH (required for deployment) + "ALGOLIASEARCH_APPLICATION_ID" + "ALGOLIASEARCH_SEARCH_ONLY_KEY" + "ALGOLIASEARCH_API_KEY" + # login via GITHUB + "GITHUB_KEY" + "GITHUB_SECRET" + "GITHUB_TOKEN" + # login via TWITTER + "TWITTER_ACCESS_TOKEN" + "TWITTER_ACCESS_TOKEN_SECRET" + "TWITTER_KEY" + "TWITTER_SECRET" + # PUSHER integration + "PUSHER_APP_ID" + "PUSHER_KEY" + "PUSHER_SECRET" + "PUSHER_CLUSTER" + # @TODO : anything else to pass forward? S3 +) +ENV_FORWARDING_DEMO_COMPULSORY_LIST=( + # ALGOLIASEARCH (required for deployment) + "ALGOLIASEARCH_APPLICATION_ID" + "ALGOLIASEARCH_SEARCH_ONLY_KEY" + "ALGOLIASEARCH_API_KEY" +) + +# +# dev.to docker command flags to pass forward +# +DEVTO_DOCKER_FLAGS="$ARG_ARRAY_STR" + +# +# Scan for ENV variabels to forward +# +echo "#---" +echo "# Lets scan for dev.to environment variables that will automatically be passed" +echo "# forward into the continaer if present (very useful for CI testing)" +echo "#---" +for i in "${ENV_FORWARDING_LIST[@]}" +do + if [[ $DEVTO_DOCKER_FLAGS == *"$i"* ]] + then + echo "[detected in arguments] - $i" + continue + fi + + if [ ! -z "$(printenv $i)" ] + then + echo "[detected env variable] - $i" + DEVTO_DOCKER_FLAGS="$DEVTO_DOCKER_FLAGS -e $i=$(printenv $i)" + continue + fi + + echo "[skipped env variable] - $i" +done + +# +# Check for DEMO compulsory list +# +if [[ "$RUN_MODE" == *"DEMO"* ]] +then + # Iterate compulsory list + for i in "${ENV_FORWARDING_DEMO_COMPULSORY_LIST[@]}" + do + # Exit if not found + if [[ $DEVTO_DOCKER_FLAGS != *"$i"* ]] + then + echo "#---" + echo "# [FATAL ERROR] Missing required DEMO env setting / argument for $i (see example above)" + echo "#---" + exit 3 + fi + done +fi + +# +# Stop and remove existing containers +# +EXISTING_POSTGRES=$(docker ps -a | grep "dev-to-postgres" | awk '{print $1}') +EXISTING_DEVTO=$(docker ps -a | grep "dev-to-app" | awk '{print $1}') +if [[ ! -z "$EXISTING_POSTGRES" ]] || [[ ! -z "$EXISTING_DEVTO" ]] +then + echo "#---" + echo "# Removing dev-to-postgres / dev-to-app containers" + echo "# Found the following : $EXISTING_POSTGRES $EXISTING_DEVTO" + echo "#---" + + # I dunno how docker does a race condition against the previous echo step + # this works around that + sleep 0.001 + + # Stopping and removing containers + docker stop $EXISTING_POSTGRES $EXISTING_DEVTO; + docker rm $EXISTING_POSTGRES $EXISTING_DEVTO; +fi + +# +# Reset the postgresql / upload folder +# +if [ "$RUN_MODE" = "RESET-DEV" ] || [ "$RUN_MODE" = "RESET-DEMO" ] +then + echo "#---" + echo "# Detected RESET based run mode : $RUN_MODE" + echo "#" + echo "# Will delete the following directory X_X " + echo "# - $UPLOAD_DIR" + echo "# - $POSTGRES_DIR" + echo "#" + echo "# And change RUN_MODE to : ${RUN_MODE:6}" + echo "#---" + + # Remove files + rm -R "$POSTGRES_DIR" + rm -R "$UPLOAD_DIR" + + # Update RUN_MODE + RUN_MODE="${RUN_MODE:6}" +fi + +# +# Build the dev-to container +# Exits on failure +# +echo "#---" +echo "# Building dev-to $RUN_MODE docker container (yay!) ... " +echo "# If this is your first time running this build step, go grab a coffee - it may take a long while" +echo "# Alternatively, some paper swords with chairs : https://xkcd.com/303/" +echo "#---" + +if [ "$RUN_MODE" = "DEV" ] +then + # Build the DEV mode container + docker build --target alpine-ruby-node -t dev-to:dev . || exit $? +else + # Build the DEMO mode container + docker build -t dev-to:demo . || exit $? +fi + +# +# Deploy postgresql container +# +echo "#---" +echo "# Deploying postgresql container" +echo "#" +echo "# POSTGRES_DIR : $POSTGRES_DIR" +echo "#---" +mkdir -p "$POSTGRES_DIR" +docker run -d --name dev-to-postgres -e POSTGRES_PASSWORD=devto -e POSTGRES_USER=devto -e POSTGRES_DB=PracticalDeveloper_development -v "$POSTGRES_DIR:/var/lib/postgresql/data" postgres:10.7-alpine + +# +# Wait for postgresql server +# this waits up to ~6*10 seconds +# +echo "#---" +echo "# Waiting for postgres server (this commonly takes 10 ~ 60 seconds) ... " +echo -n "# ." +RETRIES=12 +until docker exec dev-to-postgres psql -U devto -d PracticalDeveloper_development -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do + echo -n "." + sleep 5 +done +echo "" +echo "# Wait completed, moving on ... " +echo "#---" + +# +# Deploy dev-to container +# +echo "#---" +echo "# Deploying dev-to container" +echo "#" +echo "# RUN_MODE : $RUN_MODE" +echo "# REPO_DIR : $REPO_DIR" +echo "# UPLOAD_DIR : $UPLOAD_DIR" +echo "#---" +mkdir -p "$UPLOAD_DIR" + +# +# in DEV mode - lets run in interactive mode +# +if [ "$RUN_MODE" = "DEV" ] +then + docker run -it -p 3000:3000 \ + --name dev-to-app \ + --link dev-to-postgres:db \ + -v "$REPO_DIR:/usr/src/app" \ + -e RUN_MODE="DEV" \ + -e DATABASE_URL=postgresql://devto:devto@db:5432/PracticalDeveloper_development \ + --entrypoint "/usr/src/app/docker-entrypoint.sh" \ + $DEVTO_DOCKER_FLAGS \ + dev-to:dev + + # End of dev mode + exit 0; +fi + +# +# in DEMO mode - lets run it in the background +# +docker run -d -p 3000:3000 \ +--name dev-to-app \ +--link dev-to-postgres:db \ +-v "$UPLOAD_DIR:/usr/src/app/public/uploads/" \ +-e RUN_MODE="DEMO" \ +-e DATABASE_URL=postgresql://devto:devto@db:5432/PracticalDeveloper_development \ +$DEVTO_DOCKER_FLAGS \ +dev-to:demo + +# +# Wait for dev.to server +# this waits up to ~20*30 seconds +# +echo "#---" +echo "# Waiting for dev.to server... " +echo "#" +echo "# this commonly takes 2 ~ 10 minutes, basically, a very long time .... =[ " + +# Side note, looped to give 4 set of distinct lines +# espeially if long wait times occur (to make it more managable) +for i in 1 2 3 4 +do + RETRIES=30 + echo -n "# ." + until docker exec dev-to-app curl -I --max-time 5 -f http://localhost:3000/ > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do + echo -n "." + sleep 5 + done + echo "" +done + +echo "# Wait completed, moving on ... " +echo "#---" + +# +# Dumping out docker info +# +echo "#---" +echo "# Displaying relevant docker information" +echo "#---" +DOCKER_INFO=$(docker ps) +echo "$DOCKER_INFO" | head -1 +echo "$DOCKER_INFO" | grep dev-to- + +# +# Finishing message +# +echo "#---" +echo "# Container deployed on port ( http://localhost:3000 )" +echo "# " +echo "# Time to dev.to(gether)" +echo "#---" diff --git a/dockerhub-readme.md b/dockerhub-readme.md new file mode 100644 index 0000000000000..516e1bd874326 --- /dev/null +++ b/dockerhub-readme.md @@ -0,0 +1,35 @@ +# How do I get this up and running quickly + +Note: You will need replace all the various `` + +```bash +# Run a postgres server configured for dev.to +docker run -d --name dev-to-postgres \ + -e POSTGRES_PASSWORD=devto \ + -e POSTGRES_USER=devto \ + -e POSTGRES_DB=PracticalDeveloper_development \ + -v ":/var/lib/postgresql/data" \ + postgres:10.7-alpine; + +# Wait about 30 seconds, to give the postgres container time to start +sleep 30 + +# +# Run the prebuilt dev.to container +# binded to localhost:3000 +# +# Algoliasearch key is a hard requirements, +# for login do consider adding github/twitter keys +# see : https://github.com/thepracticaldev/dev.to/blob/master/config/sample_application.yml +# +docker run -d -p 3000:3000 \ + --name dev-to-app \ + --link dev-to-postgres:db \ + -v ":/usr/src/app/public/uploads/" \ + -e ALGOLIASEARCH_APPLICATION_ID= \ + -e ALGOLIASEARCH_SEARCH_ONLY_KEY= \ + -e ALGOLIASEARCH_API_KEY= \ + uilicious/dev.to +``` + +> PS : Someone from official dev.to team should create their own container namespace and update this segment after merger (if any)