diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f5f1360d44..8c1a8f3d50 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,8 @@ { "name": "Chemotion Dockerfile", "dockerComposeFile": [ - "../docker-compose.dev.yml" + "docker-compose.dev.yml", + "docker-compose.vs.yml" ], "service": "app", "workspaceFolder": "/home/chemotion-dev/app", @@ -51,14 +52,10 @@ "/bin/bash", ".devcontainer/pre_create.sh" ], - "postCreateCommand": [ - "/bin/bash", - "prepare-ruby-dev.sh" - ], "remoteUser": "chemotion-dev", // see https: //aka.ms/vscode-remote/containers/non-root "shutdownAction": "stopCompose", // stop compose when quitting "overrideCommand": true, // The 'app' container (which vscode is using as the devontainer) dies, as soon as we are killing the rails server that is started in the 'app' container command. To prevent this we override the 'app' container command from the the compose file. "containerEnv": { "RAILS_ENV": "development" }, -} \ No newline at end of file +} diff --git a/.devcontainer/pre_create.sh b/.devcontainer/pre_create.sh index 968595f728..1d4558b050 100644 --- a/.devcontainer/pre_create.sh +++ b/.devcontainer/pre_create.sh @@ -1,7 +1,43 @@ #!/bin/bash +if [ -f .env ]; then + echo ".env already exists" +else + if [ -f .env.example ]; then + cp .env.example .env + else + echo "No .env.example file found" + touch .env + fi +fi + +if [ -f .dockerenv ]; then + echo "Using .dockerenv to create .devcontainer/.env" + cp .dockerenv .devcontainer/.env +elif [ -f ./.dockerenv.example ]; then + echo "Using .dockerenv.example to create .devcontainer/.env" + cp .dockerenv.example .devcontainer/.env +else + echo "Neither .dockerenv nor .dockerenv.example found. Exiting." + exit 1 +fi + +# Step 2: Check if ./.env exists and append its contents to .devcontainer/.env +if [ -f .env ]; then + echo "Appending .env contents to .devcontainer/.env" + cat .env >> .devcontainer/.env +else + echo "No .env file found in the current directory." +fi + +cat .env.development >> .devcontainer/.env + +echo ".devcontainer/.env created successfully." + +cp docker-compose.dev.yml .devcontainer/docker-compose.dev.yml +cp Dockerfile.chemotion-dev .devcontainer/Dockerfile.chemotion-dev + # enable configuration files -cp .env.development .env cp public/welcome-message-sample.md public/welcome-message.md cp config/datacollectors.yml.example config/datacollectors.yml cp config/storage.yml.example config/storage.yml diff --git a/.dockerenv.example b/.dockerenv.example new file mode 100644 index 0000000000..b7876684a0 --- /dev/null +++ b/.dockerenv.example @@ -0,0 +1,38 @@ +## Environment variables for docker-compose.dev.yml +## Copy this file to .dockerenv and adjust the values +## Do not commit .dockerenv to the repository +## `docker compose --env-file .dockerenv -f docker-compose.dev.yml config` +## will use the values from this file +## - DOCKER_PG_IMAGE: the image to use as base for the db container +## - DOCKER_DEV_IMAGE: the image to use as base for the app and webpacker container +## overwriten to '' when using vs decontainer.json to avoid tag conflicts +## Latest available version: https://hub.docker.com/u/complat/dev/tags +## app image with preinstalled asdf plugins(ruby, nodejs), gems and nodejs packages + +DOCKER_DEV_IMAGE=complat/dev:v1.10.3-37-ga95534401 +#DOCKER_PG_IMAGE=postgres:16 + +## - VOLUME_NAME_HOMEDIR: Use another named volume for homedir (asdf, gems, etc) +## - VOLUME_NAME_DB: or database + +#VOLUME_NAME_HOMEDIR=chemotion_eln_homedir2 +#VOLUME_NAME_DB=chemotion_eln_database2 + +## ENV for the app container +## - RAKE_DB_MIGRATE: use by prepare sh to run db migration (rake db:migrate) +## when starting the app container {always, once, never} +## always: run db migration on every start +## once: run db migration only once after the db is created +## never: never run db migration on start +RAKE_DB_MIGRATE=once + +## Build Arguments for Dockerfile.chemotion-dev +## - FULL_BUILD: if building, this will trigger the installation of asdf +## (ruby+nodejs) and the app dependecies (gems + node packages) +## - BRANCH=branch_name: in combination with FULL_BUILD=true, this will checkout +## the specified branch before building to get the relevant prepare scripts and +## and app dependencies list (Gemfile, package.json) +## - source_image: the image to use as base for the app image +#FULL_BUILD=true +#BRANCH=main +#source_image=${DOCKER_DEV_IMAGE:-ubuntu:jammy} diff --git a/.gitignore b/.gitignore index 166edb19da..2dea6fb3af 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,9 @@ .env .env.test +.dockerenv +/.devcontainer/Dockerfile* +/.devcontainer/docker-compose* /config/matrices.json /config/mailcollector.yml diff --git a/.tool-versions b/.tool-versions index a2c2b48d42..2b8647009c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -nodejs 18.20.4 18.20.3 +nodejs 18.20.6 18.20.5 ruby 2.7.8 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9c0b82451a..fc4339ca49 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -53,6 +53,18 @@ "label": "mocha - unit tests current file", "type": "shell", "command": "NODE_PATH=./spec/javascripts:./app/javascript yarn mocha --watch --exit --require '@babel/register' './spec/javascripts/helper/setup.js' '${file}'" + }, + { + "label": "Dev Docker Compose Up", + "type": "shell", + "command": "docker", + "args": ["compose", "-f", "docker-compose.dev.yml", "--env-file", ".dockerenv", "up", "-d"] + }, + { + "label": "Dev Docker Compose Down", + "type": "shell", + "command": "docker", + "args": ["compose", "-f", "docker-compose.dev.yml", "--env-file", ".dockerenv", "down", "--remove-orphans"] } ] } diff --git a/Dockerfile.chemotion-dev b/Dockerfile.chemotion-dev index 03022f78ef..b257f4d823 100644 --- a/Dockerfile.chemotion-dev +++ b/Dockerfile.chemotion-dev @@ -2,13 +2,15 @@ # It builds a container with all the necessary gems to run chemotion ELN # WARNING: Building this container initially takes a lot of time, due to gem compiling, so grab a coffee # and write some documentation meanwhile ;) - -FROM --platform=linux/amd64 ubuntu:jammy +FROM --platform=linux/amd64 ubuntu:jammy AS base ARG DEBIAN_FRONTEND=noninteractive - -RUN set -xe && apt-get update -yqqq --fix-missing && apt-get upgrade -y -RUN apt update && apt-get install -yqq --fix-missing bash ca-certificates wget apt-transport-https git gpg\ +ARG USERNAME=chemotion-dev +RUN set -xe \ + && apt-get update -yqq --fix-missing \ + && apt-get upgrade -y \ + && apt-get install -yqq --no-install-recommends \ + bash ca-certificates wget apt-transport-https git gpg\ imagemagick libmagic-dev libmagickcore-dev libmagickwand-dev curl gnupg2 \ build-essential sudo postgresql-client libappindicator1 swig \ gconf-service libasound2 libgconf-2-4 cmake \ @@ -22,20 +24,57 @@ RUN apt update && apt-get install -yqq --fix-missing bash ca-certificates wget a fonts-dejavu fonts-dejavu-core fonts-dejavu-extra fonts-liberation2 fonts-liberation \ fonts-linuxlibertine fonts-noto-core fonts-noto-extra fonts-noto-ui-core \ fonts-opensymbol fonts-sil-gentium fonts-sil-gentium-basic inkscape \ - libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libxtst6 xauth xvfb nano jq -RUN apt-get clean \ + libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libxtst6 xauth xvfb nano jq \ + && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN useradd -ms /bin/bash chemotion-dev \ - && echo "chemotion-dev ALL=NOPASSWD: ALL" >> /etc/sudoers + && echo "chemotion-dev ALL=(ALL) ALL=NOPASSWD: ALL" >> /etc/sudoers + +ARG source_image=ubuntu:jammy +FROM ${source_image} AS asdf-source +RUN mkdir -p /home/$USERNAME/.asdf + +FROM base AS chemotion-dev +ARG FULL_BUILD=false +ARG BRANCH=main + +COPY --from=asdf-source /home/$USERNAME/.asdf /home/$USERNAME/.asdf +RUN chown chemotion-dev:chemotion-dev /home/$USERNAME/.asdf -USER chemotion-dev -WORKDIR /home/chemotion-dev +USER $USERNAME +WORKDIR /home/$USERNAME # Create node modules folder OUTSIDE of application directory -RUN mkdir /home/chemotion-dev/node_modules +RUN mkdir /home/$USERNAME/node_modules SHELL ["/bin/bash", "-c"] # Even if asdf and the related tools are only installed by running run-ruby-dev.sh, we set the PATH variables here, so when we enter the container via docker exec, we have the path set correctly -ENV ASDF_DIR=/home/chemotion-dev/.asdf -ENV PATH=/home/chemotion-dev/.asdf/shims:/home/chemotion-dev/.asdf/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV ASDF_DIR=/home/$USERNAME/.asdf +ENV PATH=/home/$USERNAME/.asdf/shims:/home/$USERNAME/.asdf/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV NODE_PATH=/home/$USERNAME/node_modules + +RUN if [ "$FULL_BUILD" = "true" ]; then \ + git -c http.verbose=true clone --depth=1 --single-branch --branch=dev-docker-compose https://github.com/complat/chemotion_ELN app ; \ + fi + +# Install asdf if FULL_BUILD is set to true +WORKDIR /home/USERNAME/app +RUN if [ "$FULL_BUILD" = "true" ]; then \ + ./prepare-asdf.sh; \ + fi + +# Install ruby and nodejs if FULL_BUILD is set to true +RUN if [ "$FULL_BUILD" = "true" ]; then \ + ./prepare-rubygems.sh; \ + fi + +# Install nodejs packages if FULL_BUILD is set to true +RUN if [ "$FULL_BUILD" = "true" ]; then \ + echo -e "--modules-folder ${NODE_PATH}\n" > /home/USERNAME/app/.yarnrc; \ + ./prepare-nodejs.sh; \ + ./prepare-nodejspkg.sh; \ + fi + +WORKDIR /home/USERNAME +RUN rm -rf /home/USERNAME/app diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 54e5e8d198..15492ef6b4 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -9,9 +9,15 @@ # - Run tests: # bundle exec rspec # +# - see .dockerenv.example for environment variables usage + +x-common-volumes: + - &vhome homedir:/home/chemotion-dev + - &vapp .:/home/chemotion-dev/app + services: postgres: - image: 'postgres:14' + image: ${DOCKER_PG_IMAGE:-postgres:14} environment: - 'POSTGRES_HOST_AUTH_METHOD=trust' expose: # expose port to app container @@ -19,27 +25,34 @@ services: ports: # expose port to host machine in case we want to use external db gui tools - '5432:5432' volumes: - - 'database:/var/lib/postgresql/data' + - database:/var/lib/postgresql/data app: build: context: '.' dockerfile: 'Dockerfile.chemotion-dev' + args: + source_image: ${DOCKER_DEV_IMAGE:-ubuntu:jammy} # Build ARG for base image + FULL_BUILD: ${FULL_BUILD:-} + image: ${DOCKER_DEV_IMAGE:-} depends_on: - 'postgres' healthcheck: test: ["CMD", "bundle", "check"] # exit 0 if all gems from Gemfile are installed, otherwise exit 1 interval: 30s timeout: 10s + env_file: + - ./.env environment: - 'SHAKAPACKER_DEV_SERVER_HOST=webpacker' - 'SHAKAPACKER_DEV_SERVER_PORT=3035' - 'THOR_SILENCE_DEPRECATION=true' + - RAKE_DB_MIGRATE=${RAKE_DB_MIGRATE:-never} ports: # expose default rails port to host machine - "3000:3000" volumes: - - 'homedir:/home/chemotion-dev/' - - '.:/home/chemotion-dev/app' + - *vhome + - *vapp working_dir: "/home/chemotion-dev/app" command: "./run-ruby-dev.sh" @@ -47,6 +60,10 @@ services: build: context: '.' dockerfile: 'Dockerfile.chemotion-dev' + args: + source_image: ${DOCKER_DEV_IMAGE:-ubuntu:jammy} # Build ARG for base image + FULL_BUILD: ${FULL_BUILD:-} + image: ${DOCKER_DEV_IMAGE:-} depends_on: app: condition: service_healthy @@ -54,10 +71,12 @@ services: - 'NODE_ENV=development' - 'SHAKAPACKER_DEV_SERVER_HOST=webpacker' - 'SHAKAPACKER_DEV_SERVER_PORT=3035' - env_file: ./.env + env_file: + - ./.env + #- ./.env.development volumes: - - 'homedir:/home/chemotion-dev/' - - '.:/home/chemotion-dev/app' + - *vhome + - *vapp ports: # expose webpacker dev server port to app container - '3035:3035' expose: @@ -67,4 +86,6 @@ services: volumes: database: + name: ${VOLUME_NAME_DB:-chemotion_eln_database} homedir: + name: ${VOLUME_NAME_HOMEDIR:-chemotion_eln_homedir} diff --git a/prepare-nodejs.sh b/prepare-nodejs.sh index 85f85087e5..d71ea0df67 100755 --- a/prepare-nodejs.sh +++ b/prepare-nodejs.sh @@ -7,6 +7,7 @@ set -e +echo '>>> check nodejs version as set in package.json: install if mismatch, and correct .tool-versions' # Get the currently installed Node.js version using asdf CURRENT_NODE_VERSION=$(asdf current nodejs 2>/dev/null | awk '{print $2}') diff --git a/prepare-nodejspkg.sh b/prepare-nodejspkg.sh index 3811d13989..9d4f285a11 100755 --- a/prepare-nodejspkg.sh +++ b/prepare-nodejspkg.sh @@ -24,5 +24,4 @@ fi echo '>>> Installing JS packages...' yarn install --production=false -yarn install diff --git a/prepare-ruby-dev.sh b/prepare-ruby-dev.sh index 280b54ed73..206439ff5d 100755 --- a/prepare-ruby-dev.sh +++ b/prepare-ruby-dev.sh @@ -7,29 +7,68 @@ export ASDF_BRANCH=v0.14.0 echo '>>> checking asdf installation' ./prepare-asdf.sh -# check nodejs version as set in package.json: install if mismatch, and correct .tool-versions' -echo '>>> check nodejs version as set in package.json: install if mismatch, and correct .tool-versions' -./prepare-nodejs.sh - # ruby gems installation ./prepare-rubygems.sh # node packages installation -./prepare-nodejspkg.sh +# ./prepare-nodejspkg.sh # prepare rails server rm -f tmp/pids/server.pid -if [ "$( psql -h postgres -U postgres -XtAc "SELECT 1 FROM pg_database WHERE datname='chemotion_dev'" )" = '1' ] +# assume default database configuration +DATABASE_NAME=${DATABASE_NAME:-chemotion_dev} +DATABASE_USER=${DATABASE_USER:-postgres} +DATABASE_HOST=${DATABASE_HOST:-postgres} +DATABASE_PORT=${DATABASE_PORT:-5432} + +# check if yq is installed +# if yq is installed parse config/database.yml file for the actual values +# if yq is not installed, then keep the set values default values +if command -v yq &> /dev/null then + DATABASE_NAME=$(yq -r .development.database config/database.yml) + DATABASE_USER=$(yq -r .development.username config/database.yml) + DATABASE_HOST=$(yq -r .development.host config/database.yml) + DATABASE_PORT=$(yq -r .development.port config/database.yml) +fi +echo "DATABASE_NAME: $DATABASE_NAME" +echo "DATABASE_USER: $DATABASE_USER" +echo "DATABASE_HOST: $DATABASE_HOST" +echo "DATABASE_PORT: $DATABASE_PORT" + +# check if the database for the given environment configuration exists +db_exists=$( psql -h $DATABASE_HOST -U $DATABASE_USER -p $DATABASE_PORT -XtAc "SELECT 1 FROM pg_database WHERE datname='$DATABASE_NAME'" ) +echo "Database exists: $db_exists" +if [ "$db_exists" = '1' ] +then + echo "===================================================" + echo "Database already exists, skipping Database creation" + echo "===================================================" + if [ "$RAKE_DB_MIGRATE" = "always" ] + then echo "================================================" - echo "Database already exists, skipping Database setup" + echo "Running 'rake db:migrate'" echo "================================================" + bundle exec rake db:migrate + fi else + # if RAKE_DB_MIGRATE is set to always or once, run rake db:setup + if [ "$RAKE_DB_MIGRATE" = "always" ] || [ "$RAKE_DB_MIGRATE" = "once" ] + then + echo "================================================" + echo "Database does not exist" + echo "running 'rake db:create/migrate/seed'" + echo "================================================" + bundle exec rake db:create + bundle exec rake db:migrate + bundle exec rake db:seed + else echo "================================================" echo "Database does not exist, running 'rake db:setup'" echo "================================================" bundle exec rake db:setup + fi fi diff --git a/run-js-dev.sh b/run-js-dev.sh index a7d4005bf7..3e74f75cad 100755 --- a/run-js-dev.sh +++ b/run-js-dev.sh @@ -1,14 +1,20 @@ #!/bin/bash -if command -v yarn; then - echo '>>> yarn is installed -> continue' +./prepare-nodejs.sh + +# if NODE_PATH is set, add --modules-folder option to .yarnrc and create a symlink to the node_modules folder +if [ -z "$NODE_PATH" ]; then + echo ">>> NODE_PATH is not set" else - echo '>>> Missing yarn. Installing...' - npm install -g yarn + echo ">>> NODE_PATH is set to $NODE_PATH" +# echo ">>> Adding --modules-folder option to .yarnrc" +# echo -e "--modules-folder $NODE_PATH" > .yarnrc +# create a symlink unless it already exists + [ -L "${HOME}/app/node_modules" ] || ln -s "${NODE_PATH}/" "${HOME}/app/node_modules" fi -echo '>>> Installing JS packages...' -yarn install + +./prepare-nodejspkg.sh echo "==========================================================================================================" echo "THIS WILL FAIL UNTIL THE RUBY GEMS ARE INSTALLED BY run-ruby-dev.sh. JUST TRY AGAIN AFTER INSTALLING THEM."