From 167e65078daefc073d05c7e63f003a09fd5945d0 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 13 Oct 2023 18:35:42 -0500 Subject: [PATCH] Add Nix development setup (#1156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm loathe to be the guy who comes in and messes with the development environment as the first thing he does, but here I am being that guy. 😅 I've been pretty into Nix for my development environments for the past couple of years for its ability to lock the full set of dev dependencies in a consistent way across machines and operating systems. I'll use this setup myself either way, but it's a bit less overhead for me if I'm able to check in the configs vs dancing around local gitignores to keep it working. Plus it gives other people the opportunity to use it! I tried to document everything nicely, keep it off to the side, and make it minimally invasive. But I totally understand if you'd rather keep this out of the repo! Just let me know. 🙇🏻 ---- Commit message: > This setup works well for me, and should allow anybody familiar with Nix to get up and running with the project really quickly. > > I documented this as an advanced development setup in the same vein as Docker, leaving the main README instructions as the happy path. > > At any point this can be safely ripped out by reverting this commit or by manually doing the following: > > 1. remove .envrc, flake.nix, flake.lock, and NIX.md > 2. clear the the extra entry in .gitignore. > 3. remove the extra configurable in application_system_test_case.rb --- .envrc | 13 +++ .gitignore | 4 + DOCKER.md | 5 + NIX.md | 89 ++++++++++++++++++ README.md | 9 +- flake.lock | 133 +++++++++++++++++++++++++++ flake.nix | 57 ++++++++++++ test/application_system_test_case.rb | 6 ++ 8 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 .envrc create mode 100644 NIX.md create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..b6373acf6 --- /dev/null +++ b/.envrc @@ -0,0 +1,13 @@ +# +# This file tells direnv, if both it and Nix are installed, to automatically +# load the Nix dependencies defined in flake.nix. +# +# For details on the Nix development setup, see NIX.md. +# +if has nix; then + if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" + fi + nix_direnv_watch_file .ruby-version + use flake +fi diff --git a/.gitignore b/.gitignore index 64d9c21a4..95702cee4 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ yarn-debug.log* gallery-dl /app/assets/builds/* !/app/assets/builds/.keep + + +# The nix development setup puts gems here +/vendor/bundle diff --git a/DOCKER.md b/DOCKER.md index eb8e92eb5..29288e503 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -1,5 +1,10 @@ # Development setup using Docker +> [!WARNING] +> This is meant for advanced users already familiar with Docker. If you're +> looking for general development instructions, please see +> [README.md](README.md). + ## Initial setup The following commands should just be run for the initial setup only. Rebuilding the docker images is only necessary when upgrading, if there are changes to the diff --git a/NIX.md b/NIX.md new file mode 100644 index 000000000..d264e10c6 --- /dev/null +++ b/NIX.md @@ -0,0 +1,89 @@ +# Development Setup using Nix + +> [!WARNING] +> This is meant for advanced users already familiar with Nix. If you're looking +> for general development instructions, please see [README.md](README.md). + +## Prerequisites + +- [Nix](https://nixos.org/download) +- [Docker w/ Compose](https://github.com/docker/compose#where-to-get-docker-compose) via Docker Desktop or equivalent - this setup runs Rails directly but uses a container for the database. +- [direnv](https://github.com/direnv/direnv) - technically optional but helpful to get the environment to automatically load when entering the project directory + +## Initial Dependencies + +If you have direnv, the `.envrc` file in the repository will get everything set +up for you automatically, you just need to run `direnv allow`. If you're not +using direnv, you can manually start a development shell with `nix develop`. + +## Database + +This setup uses the development database configured in `docker-compose.yml`, +which can be started like this: + +```shell-session +$ docker compose up -d database +``` + +The database is working properly if this command produces a list of databases: + +```shell-session +$ psql -l "$DATABASE_URL" +``` + +## Bootstrapping + +Use the `setup` script to get started for the first time. + +```shell-session +$ bin/setup +``` + +This script will: + +- Fetch Ruby and NPM dependencies +- Bootstrap the development database +- Load some sample data into the development database + +The setup script doesn't create the test database, so do that separately: + +```shell-session +$ bin/rails db:create:all +``` + +## Asset Compilation + +You can get the assets automatically compiling on file changes by leaving this +command running in a shell: + +```shell-session +$ bin/dev -m all=1,web=0 +``` + +Note this skips the Rails server, which we run separately below. This is +because something is preventing Rails from loading properly within the Foreman +environment. If somebody figures this out update this guide! + +## Development Server + +Run the Rails server in a separate shell: + +```shell-session +$ bin/rails server +``` + +## Running Tests + +All tests should be able to run normally: + +```shell-session +# Unit tests +$ bin/rails test + +# Selenium tests +$ bin/rails test:system +``` + +The Selenium tests are run by having Nix install Chromium and ChromeDriver and +set a `SELENIUM_CHROME_BINARY` environment variable to point Selenium at +Chromium in `test/application_system_test_case.rb`. diff --git a/README.md b/README.md index df9764c1b..63802be6d 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,6 @@ The following third party services are used: Once you've completed the setup below, you can login to the app using `admin@example.com` and `password` to see the admin interface. -We generally advise folks to avoid Docker for local development unless it is something thet are already very comfortable with. See [DOCKER.md](DOCKER.md) for instructions on setting up your environment using Docker. For non-Docker installations, follow the instructions below. - ### Setting up Circulate on your machine If you're new to Ruby or Rails applications, a recommended way to get set up is to use the [GoRails setup guide](https://gorails.com/setup). On that page you can select your operating system and the versions of Ruby and Rails you want to setup. It's worth going through the entire tutorial if you haven't worked on a Ruby on Rails application on your computer already as it is easier to sort through possible issues before getting into a large project like Circulate. It will take about 30 minutes to complete this tutorial. @@ -235,6 +233,13 @@ the second seed library, Denver Tool Library, by appending `.denver` to the user All of the seed user passwords are the word "password". +### Alternative Development Setups + +We generally advise you to _avoid_ these alternative development setups unless you are already very comfortable with them. The above development instructions should be better for most users. + +* **Docker:**: See [DOCKER.md](DOCKER.md) for instructions on setting up your environment using Docker. +* **Nix:** See [NIX.md](NIX.md) for details on installing dependencies with Nix. + ## Deployment Circulate is currently running on Heroku in production, but it should run fairly well anywhere Rails applications can be run. diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..038bfab38 --- /dev/null +++ b/flake.lock @@ -0,0 +1,133 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1696983906, + "narHash": "sha256-L7GyeErguS7Pg4h8nK0wGlcUTbfUMDu+HMf1UcyP72k=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "bd1cde45c77891214131cbbea5b1203e485a9d51", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-ruby": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1695879717, + "narHash": "sha256-r7q/6XOYptP1TbPR09p2olGKJEa9DKqsMCX5o6Myrtk=", + "owner": "bobvanderlinden", + "repo": "nixpkgs-ruby", + "rev": "a8612cd33ac2944cae93894bd0077e30c6b0f6e9", + "type": "github" + }, + "original": { + "owner": "bobvanderlinden", + "repo": "nixpkgs-ruby", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "nixpkgs-ruby": "nixpkgs-ruby" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..b0d775996 --- /dev/null +++ b/flake.nix @@ -0,0 +1,57 @@ +# +# This file defines dependencies for the Nix development environment. +# +# For details on the Nix development setup, see NIX.md. +# +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05"; + nixpkgs-ruby.url = "github:bobvanderlinden/nixpkgs-ruby"; + nixpkgs-ruby.inputs.nixpkgs.follows = "nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, nixpkgs-ruby, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + # Install a Ruby version matching .ruby-version + ruby = nixpkgs-ruby.lib.packageFromRubyVersionFile { + file = ./.ruby-version; + inherit system; + }; + in + { + devShell = with pkgs; + mkShell { + buildInputs = [ + ruby + yarn + foreman + + # needed to build pg gem, even though we'll run the db from Docker + postgresql + + # lib/certificate/generator.rb shells out to convert + imagemagick + + # for selenium tests + chromium + chromedriver + ]; + + # Keep gems installed in a subdirectory + BUNDLE_PATH = "./vendor/bundle"; + + # Point to Docker-hosted db server, using the same settings as + # config/docker.env + DATABASE_URL = "postgres://localhost:5435"; + PGUSER = "postgres"; + + # We'll run chromium in headless mode + HEADLESS = "true"; + PARALLEL_WORKERS = "3"; + SELENIUM_CHROME_BINARY = "${pkgs.chromium}/bin/chromium"; + }; + }); +} diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb index 2a7714add..3ac0d3177 100644 --- a/test/application_system_test_case.rb +++ b/test/application_system_test_case.rb @@ -30,6 +30,12 @@ opts.add_argument "--disable-gpu" opts.add_argument "--disable-dev-shm-usage" opts.add_argument "--window-size=1400x1800" + + # This configurable is used in the Nix development set up, see NIX.md. + # It's safe to remove if the Nix pieces are ever ripped out. + if ENV.has_key?("SELENIUM_CHROME_BINARY") + opts.binary = ENV["SELENIUM_CHROME_BINARY"] + end end ) end