From 5c95fca5cfc135672719acfff34e40b9daad9f26 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Tue, 15 Dec 2020 14:49:47 -0500 Subject: [PATCH] Tests: replace TravisCI with GitHub Actions After years of providing an awesome service for free, which we are very grateful for, TravisCI will be dropping their free plan on Dec 31. So, we are switching to GHA. Drops multi-db (foo/bar) tests. Managing three databases per RDBMS was turning into a huge hassle, and they needed to be rewritten anyway for rails 6, per Eileen's talk. --- .github/CONTRIBUTING.md | 10 ++ .github/workflows/test.yml | 158 ++++++++++++++++++++ .rspec | 1 + .rubocop.yml | 2 +- .travis.yml | 37 ----- Rakefile | 57 +++---- spec/dummy_app/bin/rails | 6 + spec/dummy_app/config/database.mysql.yml | 21 +-- spec/dummy_app/config/database.postgres.yml | 19 +-- spec/dummy_app/config/database.sqlite.yml | 8 - spec/paper_trail/version_concern_spec.rb | 29 ---- spec/support/alt_db_init.rb | 62 -------- 12 files changed, 220 insertions(+), 190 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml create mode 100755 spec/dummy_app/bin/rails delete mode 100644 spec/paper_trail/version_concern_spec.rb delete mode 100644 spec/support/alt_db_init.rb diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f839620f..d1bad500 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -73,6 +73,16 @@ createuser --superuser postgres DB=postgres bundle exec appraisal rails-5.2 rake ``` +## The dummy_app + +In the rare event you need a `console` in the `dummy_app`: + +``` +cd spec/dummy_app +cp config/database.mysql.yml config/database.yml +BUNDLE_GEMFILE='../../gemfiles/rails_5.2.gemfile' bin/rails console -e test +``` + ## Adding new schema Edit `spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb`. Migration diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..f702a414 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,158 @@ +name: gha-workflow-pt-test +on: [push, pull_request] +jobs: + + # Linting is a separate job, primary because it only needs to be done once, + # and secondarily because jobs are performed concurrently. + gha-job-pt-lint: + name: Lint + runs-on: ubuntu-18.04 + steps: + - name: Checkout source + uses: actions/checkout@v2 + - name: Setup ruby + uses: actions/setup-ruby@v1 + with: + # Set to `TargetRubyVersion` in `.rubocopy.yml` + ruby-version: 2.4 + - name: Bundle + run: | + gem install bundler + bundle install --jobs 4 --retry 3 + - name: Lint + run: bundle exec rubocop + + # The test job is a matrix of ruby/rails versions. + gha-job-pt-test: + name: Ruby ${{ matrix.ruby }}, ${{ matrix.gemfile }}.gemfile + runs-on: ubuntu-18.04 + services: + gha-service-pt-mysql: + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: paper_trail_test + image: mysql:8.0 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + ports: + - 3306:3306 + gha-service-pt-postgres: + env: + POSTGRES_PASSWORD: asdfasdf + image: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + strategy: + # Unlike TravisCI, the database will not be part of the matrix. Each + # sub-job in the matrix tests all three databases. Alternatively, we could + # have set this up with each database as a separate job, but then we'd be + # duplicating the matrix configuration three times. + matrix: + gemfile: [ 'rails_5.2', 'rails_6.0', 'rails_6.1' ] + + # To keep matrix size down, only test highest and lowest rubies. In + # `.rubocopy.yml`, set `TargetRubyVersion`, to the lowest ruby version + # tested here. + ruby: [ '2.4', '2.7' ] + + exclude: + # rails 6 requires ruby >= 2.5.0 + - ruby: '2.4' + gemfile: 'rails_6.0' + - ruby: '2.4' + gemfile: 'rails_6.1' + steps: + - name: Checkout source + uses: actions/checkout@v2 + - name: Setup ruby + uses: actions/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - name: Test connection to postgres, print lists of dbs, users + run: | + psql \ + --command '\l' \ + --command '\du' \ + --host=$POSTGRES_HOST \ + --port=$POSTGRES_PORT \ + --username=postgres \ + postgres + env: + PGPASSWORD: asdfasdf + POSTGRES_HOST: localhost + POSTGRES_PORT: 5432 + - name: Test connection to mysql + run: | + mysql \ + --execute='show databases;' \ + --host=$MYSQL_HOST \ + --protocol=TCP \ + --port=$MYSQL_PORT \ + --user=$MYSQL_USER \ + $MYSQL_DATABASE + env: + MYSQL_DATABASE: paper_trail_test + MYSQL_HOST: localhost + MYSQL_PORT: 3306 + MYSQL_USER: root + - name: Bundle + run: | + gem install bundler + bundle install --jobs 4 --retry 3 + env: + BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile + + # MySQL db was created above, sqlite will be created during test suite, + # when migrations occur, so we only need to create the postgres db. I + # tried something like `cd .....dummy_app && ....db:create`, but couldn't + # get that to work. + - name: Create postgres database + run: | + createdb \ + --host=$POSTGRES_HOST \ + --port=$POSTGRES_PORT \ + --username=postgres \ + paper_trail_test + env: + PGPASSWORD: asdfasdf + POSTGRES_HOST: localhost + POSTGRES_PORT: 5432 + + # The following three steps finally run the tests. We use `rake + # install_database_yml spec` instead of `rake` (default) because the + # default includes rubocop, which we run (once) as a separate job. See + # above. + - name: Test, sqlite + run: bundle exec rake install_database_yml spec + env: + BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile + DB: sqlite + - name: Test, mysql + run: bundle exec rake install_database_yml spec + env: + BACKTRACE: 1 + BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile + DB: mysql + PT_CI_DATABASE: paper_trail + PT_CI_DB_USER: root + PT_CI_DB_HOST: 127.0.0.1 + PT_CI_DB_PORT: 3306 + - name: Test, postgres + run: bundle exec rake install_database_yml spec + env: + BACKTRACE: 1 + BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile + DB: postgres + PT_CI_DATABASE: paper_trail + PT_CI_DB_USER: postgres + PT_CI_DB_PASSWORD: asdfasdf + PT_CI_DB_HOST: 127.0.0.1 + PT_CI_DB_PORT: 5432 diff --git a/.rspec b/.rspec index 83e16f80..2b70b733 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,3 @@ +--backtrace --color --require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index 3417aef0..6578d71b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -18,7 +18,7 @@ AllCops: # Enable pending cops so we can adopt the code before they are switched on. NewCops: enable - # Set to lowest supported version + # Set to lowest supported version of ruby TargetRubyVersion: 2.4 Bundler/OrderedGems: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 386f3560..00000000 --- a/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: ruby -cache: bundler - -# For ruby, we test the highest and lowest minor versions only. -rvm: - - 2.4 - - 2.7 - -env: - global: - - TRAVIS=true - matrix: - - DB=mysql - - DB=postgres - - DB=sqlite - -# Travis recommend we use the VM infrastructure (`sudo: required`) -sudo: required - -before_install: - - gem update bundler - -gemfile: - - gemfiles/rails_5.2.gemfile - - gemfiles/rails_6.0.gemfile - - gemfiles/rails_6.1.gemfile -matrix: - exclude: - # rails 6 requires ruby >= 2.5.0 - - rvm: 2.4 - gemfile: gemfiles/rails_6.0.gemfile - - rvm: 2.4 - gemfile: gemfiles/rails_6.1.gemfile - fast_finish: true -services: - - mysql - - postgresql diff --git a/Rakefile b/Rakefile index ba1eab5a..3f814c5f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,24 +1,36 @@ # frozen_string_literal: true +ENV["DB"] ||= "sqlite" + require "fileutils" require "bundler" Bundler::GemHelper.install_tasks -desc "Delete generated files and databases" -task :clean do +desc "Copy the database.DB.yml per ENV['DB']" +task :install_database_yml do + puts format("installing database.yml for %s", ENV["DB"]) + # It's tempting to use `git clean` here, but this rake task will be run by # people working on changes that haven't been committed yet, so we have to # be more selective with what we delete. ::FileUtils.rm("spec/dummy_app/db/database.yml", force: true) + + FileUtils.cp( + "spec/dummy_app/config/database.#{ENV['DB']}.yml", + "spec/dummy_app/config/database.yml" + ) +end + +desc "Delete generated files and databases" +task :clean do + puts format("dropping %s database", ENV["DB"]) case ENV["DB"] when "mysql" - %w[test foo bar].each do |db| - system("mysqladmin drop -f paper_trail_#{db} > /dev/null 2>&1") - end + # TODO: only works locally. doesn't respect database.yml + system "mysqladmin drop -f paper_trail_test > /dev/null 2>&1" when "postgres" - %w[test foo bar].each do |db| - system("dropdb --if-exists paper_trail_#{db} > /dev/null 2>&1") - end + # TODO: only works locally. doesn't respect database.yml + system "dropdb --if-exists paper_trail_test > /dev/null 2>&1" when nil, "sqlite" ::FileUtils.rm(::Dir.glob("spec/dummy_app/db/*.sqlite3")) else @@ -26,28 +38,21 @@ task :clean do end end -desc "Write a database.yml for the specified RDBMS" -task prepare: [:clean] do - ENV["DB"] ||= "sqlite" - FileUtils.cp( - "spec/dummy_app/config/database.#{ENV['DB']}.yml", - "spec/dummy_app/config/database.yml" - ) +desc <<~EOS + Write a database.yml for the specified RDBMS, and create database. Does not + migrate. Migration happens later in spec_helper. +EOS +task prepare: %i[clean install_database_yml] do + puts format("creating %s database", ENV["DB"]) case ENV["DB"] when "mysql" - %w[test foo bar].each do |db| - system("mysqladmin create paper_trail_#{db}") - # Migration happens later in spec_helper. - end + # TODO: only works locally. doesn't respect database.yml + system "mysqladmin create paper_trail_test" when "postgres" - %w[test foo bar].each do |db| - system("createdb paper_trail_#{db}") - # Migration happens later in spec_helper. - end + # TODO: only works locally. doesn't respect database.yml + system "createdb paper_trail_test" when nil, "sqlite" - # noop. test.sqlite3 will be created when migration happens in spec_helper. - # Shortly thereafter, foo and bar.sqlite3 are created when - # spec/support/alt_db_init.rb is `require`d. + # noop. test.sqlite3 will be created when migration happens nil else raise "Don't know how to create specified DB: #{ENV['DB']}" diff --git a/spec/dummy_app/bin/rails b/spec/dummy_app/bin/rails new file mode 100755 index 00000000..22f2d8de --- /dev/null +++ b/spec/dummy_app/bin/rails @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/spec/dummy_app/config/database.mysql.yml b/spec/dummy_app/config/database.mysql.yml index d136e2dc..71936b3c 100644 --- a/spec/dummy_app/config/database.mysql.yml +++ b/spec/dummy_app/config/database.mysql.yml @@ -1,19 +1,12 @@ test: &test adapter: mysql2 encoding: utf8 - database: paper_trail_test + database: <%= ENV.fetch('PT_CI_DATABASE', 'paper_trail') %>_test pool: 5 - username: root + username: <%= ENV.fetch('PT_CI_DB_USER', 'root') %> + host: <%= ENV.fetch('PT_CI_DB_HOST', 'localhost') %> + port: <%= ENV.fetch('PT_CI_DB_PORT', 3306) %> + protocol: TCP + + # password deliberately blank password: - host: localhost - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -foo: - <<: *test - database: paper_trail_foo - -bar: - <<: *test - database: paper_trail_bar diff --git a/spec/dummy_app/config/database.postgres.yml b/spec/dummy_app/config/database.postgres.yml index 22e813d4..f9282ea1 100644 --- a/spec/dummy_app/config/database.postgres.yml +++ b/spec/dummy_app/config/database.postgres.yml @@ -1,15 +1,8 @@ test: &test adapter: postgresql - database: paper_trail_test - username: postgres - password: - host: localhost - port: 5432 - -foo: - <<: *test - database: paper_trail_foo - -bar: - <<: *test - database: paper_trail_bar + database: <%= ENV.fetch('PT_CI_DATABASE', 'paper_trail') %>_test + username: <%= ENV.fetch('PT_CI_DB_USER', 'postgres') %> + password: <%= ENV.fetch('PT_CI_DB_PASSWORD', '') %> + host: <%= ENV.fetch('PT_CI_DB_HOST', 'localhost') %> + port: <%= ENV.fetch('PT_CI_DB_PORT', 5432) %> + protocol: TCP diff --git a/spec/dummy_app/config/database.sqlite.yml b/spec/dummy_app/config/database.sqlite.yml index 25a6c64a..30984f8b 100644 --- a/spec/dummy_app/config/database.sqlite.yml +++ b/spec/dummy_app/config/database.sqlite.yml @@ -5,11 +5,3 @@ test: &test pool: 5 timeout: 5000 database: db/test.sqlite3 - -foo: - <<: *test - database: db/test-foo.sqlite3 - -bar: - <<: *test - database: db/test-bar.sqlite3 diff --git a/spec/paper_trail/version_concern_spec.rb b/spec/paper_trail/version_concern_spec.rb deleted file mode 100644 index 2e8db936..00000000 --- a/spec/paper_trail/version_concern_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" -require "support/alt_db_init" - -RSpec.describe PaperTrail::VersionConcern do - it "allows included class to have different connections" do - expect(Foo::Version.connection).not_to eq(Bar::Version.connection) - end - - it "allows custom version class to share connection with superclass" do - expect(Foo::Version.connection).to eq(Foo::Document.connection) - expect(Bar::Version.connection).to eq(Bar::Document.connection) - end - - it "can be used with class_name option" do - expect(Foo::Document.version_class_name).to eq("Foo::Version") - expect(Bar::Document.version_class_name).to eq("Bar::Version") - end - - describe "persistence", versioning: true do - it "stores versions in the correct corresponding db location" do - foo_doc = Foo::Document.create!(name: "foobar") - bar_doc = Bar::Document.create!(name: "raboof") - expect(foo_doc.versions.first).to be_instance_of(Foo::Version) - expect(bar_doc.versions.first).to be_instance_of(Bar::Version) - end - end -end diff --git a/spec/support/alt_db_init.rb b/spec/support/alt_db_init.rb deleted file mode 100644 index 6051d3b1..00000000 --- a/spec/support/alt_db_init.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -require_relative "paper_trail_spec_migrator" - -# This file copies the test database into locations for the `Foo` and `Bar` -# namespace, then defines those namespaces, then establishes the sqlite3 -# connection for the namespaces to simulate an application with multiple -# database connections. - -# This is all going to change in rails 6. See "RailsConf 2018: Keynote: The -# Future of Rails 6: Scalable by Default by Eileen Uchitelle" -# https://www.youtube.com/watch?v=8evXWvM4oXM - -# Load database yaml to use -configs = YAML.load_file("#{Rails.root}/config/database.yml") - -# If we are testing with sqlite make it quick -db_directory = "#{Rails.root}/db" - -# Set up alternate databases -if ENV["DB"] == "sqlite" - FileUtils.cp "#{db_directory}/test.sqlite3", "#{db_directory}/test-foo.sqlite3" - FileUtils.cp "#{db_directory}/test.sqlite3", "#{db_directory}/test-bar.sqlite3" -end - -module Foo - class Base < ActiveRecord::Base - self.abstract_class = true - end - - class Version < Base - include PaperTrail::VersionConcern - end - - class Document < Base - has_paper_trail versions: { class_name: "Foo::Version" } - end -end - -Foo::Base.configurations = configs -Foo::Base.establish_connection(:foo) -ActiveRecord::Base.establish_connection(:foo) -::PaperTrailSpecMigrator.new.migrate - -module Bar - class Base < ActiveRecord::Base - self.abstract_class = true - end - - class Version < Base - include PaperTrail::VersionConcern - end - - class Document < Base - has_paper_trail versions: { class_name: "Bar::Version" } - end -end - -Bar::Base.configurations = configs -Bar::Base.establish_connection(:bar) -ActiveRecord::Base.establish_connection(:bar) -::PaperTrailSpecMigrator.new.migrate