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.
This commit is contained in:
Jared Beck 2020-12-15 14:49:47 -05:00
parent 799b590c2b
commit 5c95fca5cf
12 changed files with 220 additions and 190 deletions

View File

@ -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

158
.github/workflows/test.yml vendored Normal file
View File

@ -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

1
.rspec
View File

@ -1,2 +1,3 @@
--backtrace
--color
--require spec_helper

View File

@ -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:

View File

@ -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

View File

@ -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']}"

6
spec/dummy_app/bin/rails Executable file
View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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