diff --git a/NEWS.md b/NEWS.md index 22e6bc65c..66e261da3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ Unreleased * Introduce `suspenders:accessibility` generator * Introduce `suspenders:inline_svg` generator * Introduce `suspenders:factories` generator +* Introduce `suspenders:advisories` generator 20230113.0 (January, 13, 2023) diff --git a/README.md b/README.md index 134520935..8dbab30f7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,18 @@ Installs [capybara_accessibility_audit] and [capybara_accessible_selectors] [capybara_accessibility_audit]: https://github.com/thoughtbot/capybara_accessibility_audit [capybara_accessible_selectors]: https://github.com/citizensadvice/capybara_accessible_selectors +### Advisories + +Show security advisories during development. + +Uses the [bundler-audit][] gem to update the local security database and +show any relevant issues with the app's dependencies. This generator is +only responsible for installing the gem and adding the Rake task. + +`bin/rails g suspenders:advisories` + + [bundler-audit]: https://github.com/rubysec/bundler-audit + ### Factories Build test data with clarity and ease. diff --git a/lib/generators/suspenders/advisories_generator.rb b/lib/generators/suspenders/advisories_generator.rb new file mode 100644 index 000000000..5d2c136fc --- /dev/null +++ b/lib/generators/suspenders/advisories_generator.rb @@ -0,0 +1,27 @@ +module Suspenders + module Generators + class AdvisoriesGenerator < Rails::Generators::Base + source_root File.expand_path("../../templates/advisories", __FILE__) + desc(<<~TEXT) + Show security advisories during development. + + Uses the `bundler-audit` gem to update the local security database and + show any relevant issues with the app's dependencies via a Rake task. + TEXT + + def add_bundler_audit + gem_group :development, :test do + gem "bundler-audit", ">= 0.7.0", require: false + end + Bundler.with_unbundled_env { run "bundle install" } + end + + def modify_rakefile + insert_into_file "Rakefile", "\nrequire \"bundler/audit/task\"", + after: 'require_relative "config/application"' + insert_into_file "Rakefile", "\nBundler::Audit::Task.new", + after: 'require "bundler/audit/task"' + end + end + end +end diff --git a/test/generators/suspenders/advisories_generator_test.rb b/test/generators/suspenders/advisories_generator_test.rb new file mode 100644 index 000000000..af4f43823 --- /dev/null +++ b/test/generators/suspenders/advisories_generator_test.rb @@ -0,0 +1,81 @@ +require "test_helper" +require "generators/suspenders/advisories_generator" + +module Suspenders + module Generators + class AdvisoriesGeneratorTest < Rails::Generators::TestCase + include Suspenders::TestHelpers + + tests Suspenders::Generators::AdvisoriesGenerator + destination Rails.root + setup :prepare_destination + teardown :restore_destination + + test "adds gems to Gemfile" do + expected_output = <<~RUBY + group :development, :test do + gem "bundler-audit", ">= 0.7.0", require: false + end + RUBY + + run_generator + + assert_file app_root("Gemfile") do |file| + assert_match(expected_output, file) + end + end + + test "installs gems with Bundler" do + output = run_generator + + assert_match(/bundle install/, output) + end + + test "generator has a description" do + description = <<~TEXT + Show security advisories during development. + + Uses the `bundler-audit` gem to update the local security database and + show any relevant issues with the app's dependencies via a Rake task. + TEXT + + assert_equal description, Suspenders::Generators::AdvisoriesGenerator.desc + end + + test "modifies Rakefile" do + touch "Rakefile" + content = <<~TEXT + require_relative "config/application" + + Rails.application.load_tasks + TEXT + File.open(app_root("Rakefile"), "w") { _1.write content } + expected_rakefile = <<~TEXT + require_relative "config/application" + require "bundler/audit/task" + Bundler::Audit::Task.new + + Rails.application.load_tasks + TEXT + + run_generator + + assert_file app_root("Rakefile") do |file| + assert_equal expected_rakefile, file + end + end + + private + + def prepare_destination + touch "Gemfile" + backup_file "Rakefile" + end + + def restore_destination + remove_file_if_exists "Gemfile" + restore_file "Rakefile" + end + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 060d7dc22..3fb50d589 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -68,8 +68,6 @@ class Application < Rails::Application restore_file "config/application.rb" end - private - def backup_file(file) FileUtils.mv app_root(file), app_root("#{file}.bak") touch file