From 55ed40e1822faa0ebd8200877929b08b769630ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 21 Jul 2023 02:39:47 +0200 Subject: [PATCH 1/3] Depend on a specific version of Node.js We're choosing version 18 because if offers support for SSL 3, just like Ruby 3.1 does. Note we're symlinking a .nvmrc file as well, in order to make it compatible with NVM. While the .nvmrc and .node-version files use different formats, they both support the syntax we're using to define the version. The code to install Node.js in the Dockerfile is a simplification of the code in the Rails Dockerfile template [1]. [1] https://github.com/rails/rails/blob/04c97aec8a/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt#L25 --- .github/workflows/tests.yml | 4 +++- .node-version | 1 + .nvmrc | 1 + Dockerfile | 10 ++++++++-- README.md | 2 +- README_ES.md | 2 +- 6 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 .node-version create mode 120000 .nvmrc diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0d819d4063d..57cb41e4a60 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,7 +40,9 @@ jobs: with: bundler-cache: true - name: Setup NPM - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 + with: + node-version-file: ".node-version" - name: Copy secrets and database files run: for i in config/*.example; do cp "$i" "${i/.example}"; done - name: Setup database diff --git a/.node-version b/.node-version new file mode 100644 index 00000000000..02c8b485edb --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +18.18.0 diff --git a/.nvmrc b/.nvmrc new file mode 120000 index 00000000000..070266a2687 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +.node-version \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 497ec81fa61..a3bfa8450e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,6 @@ RUN apt-get update -qq \ libpq-dev \ libxss1 \ memcached \ - nodejs \ pkg-config \ postgresql-client \ shared-mime-info \ @@ -28,7 +27,7 @@ RUN adduser --shell /bin/bash --disabled-password --gecos "" consul \ && adduser consul sudo \ && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers -RUN echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bundle/bin"' > /etc/sudoers.d/secure_path +RUN echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bundle/bin:/usr/local/node/bin"' > /etc/sudoers.d/secure_path RUN chmod 0440 /etc/sudoers.d/secure_path # Define where our application will live inside the image @@ -40,6 +39,13 @@ RUN mkdir -p $RAILS_ROOT/tmp/pids # Set our working directory inside the image WORKDIR $RAILS_ROOT +# Install Node +COPY .node-version ./ +ENV PATH=/usr/local/node/bin:$PATH +RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ + /tmp/node-build-master/bin/node-build `cat .node-version` /usr/local/node && \ + rm -rf /tmp/node-build-master + # Use the Gemfiles as Docker cache markers. Always bundle before copying app src. # (the src likely changed and we don't want to invalidate Docker's cache too early) COPY Gemfile* ./ diff --git a/README.md b/README.md index 5325b10e391..5d580d5ce33 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ You can access the main website of the project at [http://consuldemocracy.org](h **NOTE**: For more detailed instructions check the [docs](https://docs.consuldemocracy.org) -Prerequisites: install git, Ruby 3.1.4, CMake, pkg-config, shared-mime-info, Node.js and PostgreSQL (>=9.5). +Prerequisites: install git, Ruby 3.1.4, CMake, pkg-config, shared-mime-info, Node.js 18.18.0 and PostgreSQL (>=9.5). ```bash git clone https://github.com/consuldemocracy/consuldemocracy.git diff --git a/README_ES.md b/README_ES.md index b82284b0e07..0704e6f98a5 100644 --- a/README_ES.md +++ b/README_ES.md @@ -36,7 +36,7 @@ Puedes acceder a la página principal del proyecto en [http://consuldemocracy.or **NOTA**: para unas instrucciones más detalladas consulta la [documentación](https://docs.consuldemocracy.org) -Prerequisitos: tener instalado git, Ruby 3.1.4, CMake, pkg-config, shared-mime-info, Node.js y PostgreSQL (9.5 o superior). +Prerequisitos: tener instalado git, Ruby 3.1.4, CMake, pkg-config, shared-mime-info, Node.js 18.18.0 y PostgreSQL (9.5 o superior). ```bash git clone https://github.com/consuldemocracy/consuldemocracy.git From 8ab66dd12f96057a3e716ccf0a1da4dc4784b278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 21 Jul 2023 03:22:58 +0200 Subject: [PATCH 2/3] Install Node.js on deploy We use FNM instead of NVM. Reason: the setup seems easier with just `eval "$(fnm env)"`. So now, we try to install Node.js; if the command fails, it could be because FNM isn't installed (and we need to install it) or because the version of Node.js cannot be installed with the current version of FNM (and we need to update FNM). After installing/updating FNM, we try to install Node.js again. Note we're using `fnm env` in the middle of the `fnm_setup_command`. That way, the command will raise a `SSHKit::Command::Failed` exception if `fnm` isn't installed, and it will give us an indicator that we need to actually install it. --- config/deploy.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/config/deploy.rb b/config/deploy.rb index b2e89a59af4..3a45f29683a 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -37,6 +37,16 @@ def main_deploy_server set :local_user, ENV["USER"] +set :fnm_path, "$HOME/.fnm" +set :fnm_install_command, "curl -fsSL https://fnm.vercel.app/install | " \ + "bash -s -- --install-dir \"#{fetch(:fnm_path)}\"" +set :fnm_update_command, "#{fetch(:fnm_install_command)} --skip-shell" +set :fnm_setup_command, -> do + "export PATH=\"#{fetch(:fnm_path)}:$PATH\" && " \ + "cd #{release_path} && fnm env > /dev/null && eval \"$(fnm env)\"" + end +set :fnm_install_node_command, -> { "#{fetch(:fnm_setup_command)} && fnm use --install-if-missing" } + set :puma_conf, "#{release_path}/config/puma/#{fetch(:rails_env)}.rb" set :delayed_job_workers, 2 @@ -49,6 +59,7 @@ def main_deploy_server Rake::Task["delayed_job:default"].clear_actions Rake::Task["puma:smart_restart"].clear_actions + after :updating, "install_node" after :updating, "install_ruby" after "deploy:migrate", "add_new_settings" @@ -89,6 +100,26 @@ def main_deploy_server end end +task :install_node do + on roles(:app) do + with rails_env: fetch(:rails_env) do + begin + execute fetch(:fnm_install_node_command) + rescue SSHKit::Command::Failed + begin + execute fetch(:fnm_setup_command) + rescue SSHKit::Command::Failed + execute fetch(:fnm_install_command) + else + execute fetch(:fnm_update_command) + end + + execute fetch(:fnm_install_node_command) + end + end + end +end + task :refresh_sitemap do on roles(:app) do within release_path do From f209325b902659bddf39e84e032c565513a9070b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Sat, 22 Jul 2023 06:42:28 +0200 Subject: [PATCH 3/3] Wrap node commands with Capistrano This code is based on what the rvm1-capistrano and capistrano-nvm gems do, but simplified a bit to take advantage of the `fnm exec` command. Since ExecJS will check for a JavaScript runtime every time the application is started, we need to include commands like `bundle` (used when running `bin/delayed_job`), `puma` and `rake`, so Node.js is found when running these commands. I'm not sure whether `pumactl` is also necessary, but I've added it for consistency. We're also including the default commands gems like capistrano-nvm use because we will add the `npm` command in the near future. Note that, in order to setup FNM, we need to enter the actual release folder (using `within release_path` isn't enough). So we have to run this task after the actual release is created; otherwise many commands would run in the previous release's folder. --- config/deploy.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/config/deploy.rb b/config/deploy.rb index 3a45f29683a..657b14f8038 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -46,6 +46,7 @@ def main_deploy_server "cd #{release_path} && fnm env > /dev/null && eval \"$(fnm env)\"" end set :fnm_install_node_command, -> { "#{fetch(:fnm_setup_command)} && fnm use --install-if-missing" } +set :fnm_map_bins, %w[bundle node npm puma pumactl rake yarn] set :puma_conf, "#{release_path}/config/puma/#{fetch(:rails_env)}.rb" @@ -59,6 +60,8 @@ def main_deploy_server Rake::Task["delayed_job:default"].clear_actions Rake::Task["puma:smart_restart"].clear_actions + after "git:create_release", "map_node_bins" + after :updating, "install_node" after :updating, "install_ruby" @@ -120,6 +123,20 @@ def main_deploy_server end end +task :map_node_bins do + on roles(:app) do + within release_path do + with rails_env: fetch(:rails_env) do + prefix = "#{fetch(:fnm_setup_command)} && fnm exec" + + fetch(:fnm_map_bins).each do |command| + SSHKit.config.command_map.prefix[command.to_sym].unshift(prefix) + end + end + end + end +end + task :refresh_sitemap do on roles(:app) do within release_path do