From c338a85a1159a5d651cc2500e2ef970a5327dffc Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Fri, 26 Jan 2024 11:24:11 -0500 Subject: [PATCH 1/8] Introduce `suspenders:setup` generator Replaces the [default setup script][] provided by Rails. The trade-off being that the implementation is simple, but the cost is we risk drifting from Rails. After attempting to use [gsub_file][] and [insert_into_file][], it felt simpler to just override the file completely. Uses [Homebrew Bundle][] in combination with [asdf][] to install system and tool dependencies locally. Re-introduces [dev:prime][] to seed development data on top of [seed][] data necessary for production. Allow caller to wipe the database clean by setting `WIPE_DATABASE=true` before running `bin/setup`. Additionally, we [setup the test environment][] to avoid issues with asset compilation. [default setup script]: https://github.com/rails/rails/blob/main/railties/lib/rails/generators/rails/app/templates/bin/setup.tt [gsub_file]: https://rubydoc.info/gems/thor/Thor/Actions#gsub_file-instance_method [insert_into_file]: https://rubydoc.info/gems/thor/Thor/Actions#insert_into_file-instance_method [Homebrew Bundle]: https://github.com/Homebrew/homebrew-bundle [asdf]: https://asdf-vm.com [dev:prime]: https://thoughtbot.com/blog/priming-the-pump [seed]: https://guides.rubyonrails.org/active_record_migrations.html#migrations-and-seed-data [setup the test environment]: https://github.com/rails/rails/pull/47719/files --- NEWS.md | 1 + README.md | 14 +++ lib/generators/suspenders/setup_generator.rb | 32 +++++++ lib/generators/templates/setup/bin_setup.rb | 59 +++++++++++++ lib/generators/templates/setup/brewfile | 9 ++ lib/generators/templates/setup/dev_prime.rb | 12 +++ .../suspenders/setup_generator_test.rb | 88 +++++++++++++++++++ 7 files changed, 215 insertions(+) create mode 100644 lib/generators/suspenders/setup_generator.rb create mode 100755 lib/generators/templates/setup/bin_setup.rb create mode 100644 lib/generators/templates/setup/brewfile create mode 100644 lib/generators/templates/setup/dev_prime.rb create mode 100644 test/generators/suspenders/setup_generator_test.rb diff --git a/NEWS.md b/NEWS.md index 54f224388..06df84db3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,7 @@ Unreleased * Introduce `suspenders:lint` generator * Introduce `suspenders:rake` generator * Introduce `suspenders:views` generator +* Introduce `suspenders:setup` generator 20230113.0 (January, 13, 2023) diff --git a/README.md b/README.md index 9d167c951..583e405bd 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,20 @@ document [lang][]. [title]: https://github.com/calebhearth/title [lang]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang +### Setup + +A holistic setup script. + +```sh +bin/setup +``` + +or + +```sh +WIPE_DATABASE=true bin/setup +``` + ## Contributing See the [CONTRIBUTING] document. diff --git a/lib/generators/suspenders/setup_generator.rb b/lib/generators/suspenders/setup_generator.rb new file mode 100644 index 000000000..4c31d6d58 --- /dev/null +++ b/lib/generators/suspenders/setup_generator.rb @@ -0,0 +1,32 @@ +module Suspenders + module Generators + class SetupGenerator < Rails::Generators::Base + source_root File.expand_path("../../templates/setup", __FILE__) + desc <<~TEXT + A holistic setup script. + + ```sh + bin/setup + ``` + + or + + ```sh + WIPE_DATABASE=true bin/setup + ``` + TEXT + + def create_brewfile + copy_file "brewfile", "Brewfile" + end + + def create_dev_prime + copy_file "dev_prime.rb", "lib/tasks/dev.rake" + end + + def replace_bin_setup + copy_file "bin_setup.rb", "bin/setup", force: true + end + end + end +end diff --git a/lib/generators/templates/setup/bin_setup.rb b/lib/generators/templates/setup/bin_setup.rb new file mode 100755 index 000000000..a8e055dd9 --- /dev/null +++ b/lib/generators/templates/setup/bin_setup.rb @@ -0,0 +1,59 @@ +#!/usr/bin/env ruby +require "fileutils" +require "open3" + +# path to your application root. +APP_ROOT = File.expand_path("..", __dir__) + +def system!(*args) + system(*args, exception: true) +end + +def using_node? + File.exist? "package.json" +end + +FileUtils.chdir APP_ROOT do + if ENV["CI"].nil? + puts "\n== Installing Homebrew Bundle from Brewfile ==" + system! "brew bundle" + + puts "\n== Starting postgresql@15 ==" + system! "brew services restart postgresql@15" + + puts "\n== Starting redis ==" + system! "brew services restart redis" + + _, _, status = Open3.capture3("which asdf") + if status.success? + puts "\n== Installing tool dependecies via asdf ==" + system! "asdf plugin-add ruby https://github.com/asdf-vm/asdf-ruby" + system! "asdf plugin-add nodejs https://github.com/asdf-vm/asdf-nodejs" if using_node? + system! "asdf plugin-add yarn https://github.com/twuni/asdf-yarn" if using_node? + system! "asdf plugin-update --all" + system! "asdf install" + end + end + + puts "\n== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") + system("yarn install --check-files") if using_node? + + puts "\n== Preparing database and adding development seed data ==" + if ENV["WIPE_DATABASE"] == true + system! "bin/rails db:reset dev:prime" + else + system! "bin/rails dev:prime" + end + + # https://github.com/rails/rails/pull/47719/files + puts "\n== Setup test environment ==" + system! "bin/rails test:prepare" + + puts "\n== Removing old logs and tempfiles ==" + system! "bin/rails log:clear tmp:clear" + + puts "\n== Restarting application server ==" + system! "bin/rails restart" +end diff --git a/lib/generators/templates/setup/brewfile b/lib/generators/templates/setup/brewfile new file mode 100644 index 000000000..7f49ca37e --- /dev/null +++ b/lib/generators/templates/setup/brewfile @@ -0,0 +1,9 @@ +# https://formulae.brew.sh/formula/vips +brew "vips" + +# https://formulae.brew.sh/formula/redis +brew "redis" + +# https://formulae.brew.sh/formula/postgresql@15 +# Change version to match production +brew "postgresql@15" diff --git a/lib/generators/templates/setup/dev_prime.rb b/lib/generators/templates/setup/dev_prime.rb new file mode 100644 index 000000000..44da79691 --- /dev/null +++ b/lib/generators/templates/setup/dev_prime.rb @@ -0,0 +1,12 @@ +if Rails.env.development? || Rails.env.test? + require "factory_bot" if Bundler.rubygems.find_name("factory_bot_rails").any? + + namespace :dev do + desc "Sample data for local development environment" + task prime: "db:setup" do + include FactoryBot::Syntax::Methods if Bundler.rubygems.find_name("factory_bot_rails").any? + + # create(:user, email: "user@example.com", password: "password") + end + end +end diff --git a/test/generators/suspenders/setup_generator_test.rb b/test/generators/suspenders/setup_generator_test.rb new file mode 100644 index 000000000..ca1fc2471 --- /dev/null +++ b/test/generators/suspenders/setup_generator_test.rb @@ -0,0 +1,88 @@ +require "test_helper" +require "generators/suspenders/setup_generator" + +module Suspenders + module Generators + class SetupGeneratorTest < Rails::Generators::TestCase + include Suspenders::TestHelpers + + tests Suspenders::Generators::SetupGenerator + destination Rails.root + setup :prepare_destination + teardown :restore_destination + + test "creates Brewfile" do + expected = <<~TEXT + # https://formulae.brew.sh/formula/vips + brew "vips" + + # https://formulae.brew.sh/formula/redis + brew "redis" + + # https://formulae.brew.sh/formula/postgresql@15 + # Change version to match production + brew "postgresql@15" + TEXT + + run_generator + + assert_file app_root("Brewfile") do |file| + assert_equal expected, file + end + end + + test "modifies bin/setup" do + expected = bin_setup + + run_generator + + assert_file app_root("bin/setup") do |file| + assert_equal expected, file + end + end + + test "creates dev:prime task" do + expected = <<~RUBY + if Rails.env.development? || Rails.env.test? + require "factory_bot" if Bundler.rubygems.find_name("factory_bot_rails").any? + + namespace :dev do + desc "Sample data for local development environment" + task prime: "db:setup" do + include FactoryBot::Syntax::Methods if Bundler.rubygems.find_name("factory_bot_rails").any? + + # create(:user, email: "user@example.com", password: "password") + end + end + end + RUBY + + run_generator + + assert_file app_root("lib/tasks/dev.rake") do |file| + assert_equal expected, file + end + end + + test "has a custom description" do + assert_no_match(/Description:/, generator_class.desc) + end + + private + + def prepare_destination + backup_file "bin/setup" + end + + def restore_destination + remove_file_if_exists "Brewfile" + restore_file "bin/setup" + remove_dir_if_exists "lib/tasks" + end + + def bin_setup + File.read("./lib/generators/templates/setup/bin_setup.rb") + end + end + end +end From d28306358f097b7b73f4cff56bd6a5e616f3dcdb Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Fri, 9 Feb 2024 05:55:58 -0500 Subject: [PATCH 2/8] Remove option to wipe database Running `dev:prime` calls `db:setup`, which appears to be wiping the database for us. I tried updating it to use `db:prepare`, but that means we will always seed the database with data from the `dev.rake` file, resulting in duplicate records. This implementation is consistent with the existing implementation. --- lib/generators/templates/setup/bin_setup.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/generators/templates/setup/bin_setup.rb b/lib/generators/templates/setup/bin_setup.rb index a8e055dd9..823bd5bc1 100755 --- a/lib/generators/templates/setup/bin_setup.rb +++ b/lib/generators/templates/setup/bin_setup.rb @@ -41,11 +41,7 @@ def using_node? system("yarn install --check-files") if using_node? puts "\n== Preparing database and adding development seed data ==" - if ENV["WIPE_DATABASE"] == true - system! "bin/rails db:reset dev:prime" - else - system! "bin/rails dev:prime" - end + system! "bin/rails dev:prime" # https://github.com/rails/rails/pull/47719/files puts "\n== Setup test environment ==" From 893ad7b13135289e1479e64516affd53a41bca43 Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Fri, 9 Feb 2024 09:19:33 -0500 Subject: [PATCH 3/8] Conditionally generate assets during setup Clobber and precompile assets during setup if using [Sprockets][]. This is helpful in cases where one might switch branches frequently, which can result in broken assets. [Sprockets]: https://github.com/rails/sprockets --- lib/generators/templates/setup/bin_setup.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/generators/templates/setup/bin_setup.rb b/lib/generators/templates/setup/bin_setup.rb index 823bd5bc1..95a195fa5 100755 --- a/lib/generators/templates/setup/bin_setup.rb +++ b/lib/generators/templates/setup/bin_setup.rb @@ -1,6 +1,7 @@ #!/usr/bin/env ruby require "fileutils" require "open3" +require "bundler" # path to your application root. APP_ROOT = File.expand_path("..", __dir__) @@ -43,6 +44,11 @@ def using_node? puts "\n== Preparing database and adding development seed data ==" system! "bin/rails dev:prime" + if Bundler.rubygems.find_name("sprockets") + puts "\n== Generating assets ==" + system! "bin/rails assets:clobber assets:precompile" + end + # https://github.com/rails/rails/pull/47719/files puts "\n== Setup test environment ==" system! "bin/rails test:prepare" From 006b133608a43a70003e73ef7d0484974cf15f86 Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Mon, 12 Feb 2024 07:27:50 -0500 Subject: [PATCH 4/8] Remove reference to wiping database --- README.md | 6 ------ lib/generators/suspenders/setup_generator.rb | 6 ------ 2 files changed, 12 deletions(-) diff --git a/README.md b/README.md index 583e405bd..721bc8ab7 100644 --- a/README.md +++ b/README.md @@ -142,12 +142,6 @@ A holistic setup script. bin/setup ``` -or - -```sh -WIPE_DATABASE=true bin/setup -``` - ## Contributing See the [CONTRIBUTING] document. diff --git a/lib/generators/suspenders/setup_generator.rb b/lib/generators/suspenders/setup_generator.rb index 4c31d6d58..45633f174 100644 --- a/lib/generators/suspenders/setup_generator.rb +++ b/lib/generators/suspenders/setup_generator.rb @@ -8,12 +8,6 @@ class SetupGenerator < Rails::Generators::Base ```sh bin/setup ``` - - or - - ```sh - WIPE_DATABASE=true bin/setup - ``` TEXT def create_brewfile From 97068cd7e4f2afae64eb37997da4991245c1cd31 Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Mon, 12 Feb 2024 07:33:08 -0500 Subject: [PATCH 5/8] Remove system setup steps We've decided this script should not conflate system and application setup. --- lib/generators/suspenders/setup_generator.rb | 4 ---- lib/generators/templates/setup/bin_setup.rb | 24 +------------------ lib/generators/templates/setup/brewfile | 9 ------- .../suspenders/setup_generator_test.rb | 20 ---------------- 4 files changed, 1 insertion(+), 56 deletions(-) delete mode 100644 lib/generators/templates/setup/brewfile diff --git a/lib/generators/suspenders/setup_generator.rb b/lib/generators/suspenders/setup_generator.rb index 45633f174..2d8aa3fd9 100644 --- a/lib/generators/suspenders/setup_generator.rb +++ b/lib/generators/suspenders/setup_generator.rb @@ -10,10 +10,6 @@ class SetupGenerator < Rails::Generators::Base ``` TEXT - def create_brewfile - copy_file "brewfile", "Brewfile" - end - def create_dev_prime copy_file "dev_prime.rb", "lib/tasks/dev.rake" end diff --git a/lib/generators/templates/setup/bin_setup.rb b/lib/generators/templates/setup/bin_setup.rb index 95a195fa5..1776dcc10 100755 --- a/lib/generators/templates/setup/bin_setup.rb +++ b/lib/generators/templates/setup/bin_setup.rb @@ -1,6 +1,5 @@ #!/usr/bin/env ruby require "fileutils" -require "open3" require "bundler" # path to your application root. @@ -15,28 +14,7 @@ def using_node? end FileUtils.chdir APP_ROOT do - if ENV["CI"].nil? - puts "\n== Installing Homebrew Bundle from Brewfile ==" - system! "brew bundle" - - puts "\n== Starting postgresql@15 ==" - system! "brew services restart postgresql@15" - - puts "\n== Starting redis ==" - system! "brew services restart redis" - - _, _, status = Open3.capture3("which asdf") - if status.success? - puts "\n== Installing tool dependecies via asdf ==" - system! "asdf plugin-add ruby https://github.com/asdf-vm/asdf-ruby" - system! "asdf plugin-add nodejs https://github.com/asdf-vm/asdf-nodejs" if using_node? - system! "asdf plugin-add yarn https://github.com/twuni/asdf-yarn" if using_node? - system! "asdf plugin-update --all" - system! "asdf install" - end - end - - puts "\n== Installing dependencies ==" + puts "== Installing dependencies ==" system! "gem install bundler --conservative" system("bundle check") || system!("bundle install") system("yarn install --check-files") if using_node? diff --git a/lib/generators/templates/setup/brewfile b/lib/generators/templates/setup/brewfile deleted file mode 100644 index 7f49ca37e..000000000 --- a/lib/generators/templates/setup/brewfile +++ /dev/null @@ -1,9 +0,0 @@ -# https://formulae.brew.sh/formula/vips -brew "vips" - -# https://formulae.brew.sh/formula/redis -brew "redis" - -# https://formulae.brew.sh/formula/postgresql@15 -# Change version to match production -brew "postgresql@15" diff --git a/test/generators/suspenders/setup_generator_test.rb b/test/generators/suspenders/setup_generator_test.rb index ca1fc2471..c6a2ce6dc 100644 --- a/test/generators/suspenders/setup_generator_test.rb +++ b/test/generators/suspenders/setup_generator_test.rb @@ -11,26 +11,6 @@ class SetupGeneratorTest < Rails::Generators::TestCase setup :prepare_destination teardown :restore_destination - test "creates Brewfile" do - expected = <<~TEXT - # https://formulae.brew.sh/formula/vips - brew "vips" - - # https://formulae.brew.sh/formula/redis - brew "redis" - - # https://formulae.brew.sh/formula/postgresql@15 - # Change version to match production - brew "postgresql@15" - TEXT - - run_generator - - assert_file app_root("Brewfile") do |file| - assert_equal expected, file - end - end - test "modifies bin/setup" do expected = bin_setup From b074e46321807765c37e30f0927fb5ffd0a05ea2 Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Wed, 14 Feb 2024 16:40:38 -0500 Subject: [PATCH 6/8] Remove `dev:prime` We'll introduce this in a separate generator. --- lib/generators/suspenders/setup_generator.rb | 4 ---- lib/generators/templates/setup/bin_setup.rb | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/generators/suspenders/setup_generator.rb b/lib/generators/suspenders/setup_generator.rb index 2d8aa3fd9..7a9fae116 100644 --- a/lib/generators/suspenders/setup_generator.rb +++ b/lib/generators/suspenders/setup_generator.rb @@ -10,10 +10,6 @@ class SetupGenerator < Rails::Generators::Base ``` TEXT - def create_dev_prime - copy_file "dev_prime.rb", "lib/tasks/dev.rake" - end - def replace_bin_setup copy_file "bin_setup.rb", "bin/setup", force: true end diff --git a/lib/generators/templates/setup/bin_setup.rb b/lib/generators/templates/setup/bin_setup.rb index 1776dcc10..3caf1f2e3 100755 --- a/lib/generators/templates/setup/bin_setup.rb +++ b/lib/generators/templates/setup/bin_setup.rb @@ -20,7 +20,11 @@ def using_node? system("yarn install --check-files") if using_node? puts "\n== Preparing database and adding development seed data ==" - system! "bin/rails dev:prime" + if File.exist? "lib/tasks/dev.rake" + system! "bin/rails dev:rake" + else + system! "bin/rails db:prepare" + end if Bundler.rubygems.find_name("sprockets") puts "\n== Generating assets ==" From f55a1d69f0c9e83cb6de5067c87a4e38df9628dd Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Wed, 14 Feb 2024 16:46:26 -0500 Subject: [PATCH 7/8] Remove obsolete test --- .../suspenders/setup_generator_test.rb | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/test/generators/suspenders/setup_generator_test.rb b/test/generators/suspenders/setup_generator_test.rb index c6a2ce6dc..e3396ba2b 100644 --- a/test/generators/suspenders/setup_generator_test.rb +++ b/test/generators/suspenders/setup_generator_test.rb @@ -21,29 +21,6 @@ class SetupGeneratorTest < Rails::Generators::TestCase end end - test "creates dev:prime task" do - expected = <<~RUBY - if Rails.env.development? || Rails.env.test? - require "factory_bot" if Bundler.rubygems.find_name("factory_bot_rails").any? - - namespace :dev do - desc "Sample data for local development environment" - task prime: "db:setup" do - include FactoryBot::Syntax::Methods if Bundler.rubygems.find_name("factory_bot_rails").any? - - # create(:user, email: "user@example.com", password: "password") - end - end - end - RUBY - - run_generator - - assert_file app_root("lib/tasks/dev.rake") do |file| - assert_equal expected, file - end - end - test "has a custom description" do assert_no_match(/Description:/, generator_class.desc) end From c010e4a8876d6ea598c4570e844fabcb37a9014a Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Thu, 15 Feb 2024 05:53:53 -0500 Subject: [PATCH 8/8] Reference correct task --- lib/generators/templates/setup/bin_setup.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generators/templates/setup/bin_setup.rb b/lib/generators/templates/setup/bin_setup.rb index 3caf1f2e3..ef20fe69e 100755 --- a/lib/generators/templates/setup/bin_setup.rb +++ b/lib/generators/templates/setup/bin_setup.rb @@ -21,7 +21,7 @@ def using_node? puts "\n== Preparing database and adding development seed data ==" if File.exist? "lib/tasks/dev.rake" - system! "bin/rails dev:rake" + system! "bin/rails dev:prime" else system! "bin/rails db:prepare" end