From 3f0cd959af029553b7699b48aa581af8c1ca46cf Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Thu, 21 Nov 2024 14:22:45 +0000 Subject: [PATCH] Separate Appraisal runs from primary test runs With Rails 8, the minimum version of Ruby becomes 3.2. Unless we drop support for prior Ruby versions (and also Rails versions), the way in which we were approaching CI can't work. Maintaining support for older Rails is not difficult because of Administrate itself, but how we approach testing so it feels unreasonable to drop support (which if we didn't test against it, we would effectively be doing) because it made CI harder to do. And we _can_ make it work, so we will. Previously, we ran the setup for the project as usual, and then ran setup for the Appraisals. This served two functions: 1. Make Appraisal available in the bundle, 2. Setup the database for running tests against However, in the CI matrix, we support multiple different versions of Ruby (3.0 and up to 3.3). As we try and add Rails 8.0, this means we can't run `bundle install` on those versions of Ruby below 3.2 as it's a dependency constraint on Rails itself. From experiments, we also can't: 1. Install Appraisal outside of the bundle, because it references gems in a way that Bundler is there to solve, 2. Load newer `schema.rb` files in older versions of Rails. Rails 7 introduced versioned schemas, and future schema versions can't be run with older versions of Rails. Instead, what we _can_ do is run the project setup inside a container configured to use the database in the CI environment, then manually do what Appraisal is doing to avoid issues bundling. This container will always use the current development version of Ruby (3.2.2, at the time of this commit) taken from the `.ruby-version` file, in which we run just enough to setup the database and ensure the later tests can run against it. It's unlikely that Administrate would do something which wasn't supported at the database level, but not impossible. However these tests would catch that (from experience: Rails falls back to a string on unknown types, so it's even less likely to fail). We also need to remove the Ruby version specification from the generated Appraisal gemfiles, as otherwise these won't install. Whilst they seemed to be valid outside of this way of running things, here, they are not. We just use `sed` to remove it in place, as it can be discarded later and removing it would cause problems with Heroku deployment of the demo app. Finally, we inline the previously extracted Action from #2524. The intent was to reduce duplication, but the order of operations are now so different it's not worth it and it would only be used in one place. Extracted from #2705. --- .github/actions/setup/action.yml | 38 ------------------------- .github/workflows/main.yml | 49 +++++++++++++++++++++++++++++--- bin/generate-database-schema | 37 ++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 42 deletions(-) delete mode 100644 .github/actions/setup/action.yml create mode 100755 bin/generate-database-schema diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml deleted file mode 100644 index 44a3513dbf..0000000000 --- a/.github/actions/setup/action.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Setup -description: Shared setup steps -inputs: - ruby-version: - required: true -runs: - using: composite - steps: - - name: Set up Ruby ${{ inputs.ruby-version }} - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ inputs.ruby-version }} - bundler-cache: true - - name: Set up JS - uses: actions/setup-node@v4 - with: - cache: yarn - - name: Install Ruby dependencies - run: bundle install - shell: bash - - name: Install Appraisal dependencies - run: bundle exec appraisal install - shell: bash - - name: Install JS dependencies - run: yarn install - shell: bash - - name: Setup the environment - run: cp .sample.env .env - shell: bash - - run: cp spec/example_app/config/database.yml.sample spec/example_app/config/database.yml - shell: bash - - name: Setup the database - run: bundle exec rake db:setup - shell: bash - - name: Build assets - run: yarn run build && yarn run build:css - shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b0e6f72279..5adc4ed783 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,9 +34,28 @@ jobs: --health-retries 5 steps: - uses: actions/checkout@v4 - - uses: ./.github/actions/setup + - name: Set up Ruby ${{ matrix.ruby }} + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Set up JS + uses: actions/setup-node@v4 + with: + cache: yarn + - name: Install Ruby dependencies + run: bundle install + - name: Install Appraisal dependencies + run: bundle exec appraisal install + - name: Install JS dependencies + run: yarn install + - name: Setup the environment + run: cp .sample.env .env + - run: cp spec/example_app/config/database.yml.sample spec/example_app/config/database.yml + - name: Setup the database + run: bundle exec rake db:setup + - name: Build assets + run: yarn run build && yarn run build:css - name: Run tests run: bundle exec rspec @@ -70,8 +89,30 @@ jobs: --health-retries 5 steps: - uses: actions/checkout@v4 - - uses: ./.github/actions/setup + - name: Setup the environment + run: cp .sample.env .env + - run: cp spec/example_app/config/database.yml.sample spec/example_app/config/database.yml + - name: Prepare main database schema + run: bin/generate-database-schema + - name: Set up Ruby ${{ matrix.ruby }} + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - - name: Appraise Rails ${{ matrix.appraisal }} - run: bundle exec appraisal ${{ matrix.appraisal }} rspec + - name: Set up JS + uses: actions/setup-node@v4 + with: + cache: yarn + - name: Remove Ruby version specification + run: sed -i 's/^ruby\(.*\)//g' gemfiles/${{ matrix.appraisal }}.gemfile + - name: Install Ruby dependencies + run: bundle install + env: + BUNDLE_GEMFILE: gemfiles/${{ matrix.appraisal }}.gemfile + - name: Install JS dependencies + run: yarn install + - name: Build assets + run: yarn run build && yarn run build:css + - name: Run tests + run: bundle exec rspec + env: + BUNDLE_GEMFILE: gemfiles/${{ matrix.appraisal }}.gemfile diff --git a/bin/generate-database-schema b/bin/generate-database-schema new file mode 100755 index 0000000000..948590899c --- /dev/null +++ b/bin/generate-database-schema @@ -0,0 +1,37 @@ +#!/bin/sh + +set -e + +run_in_container() { + container_id=$1 + shift + cmd=$* + + docker exec --workdir /app "$container_id" bash -c "$cmd" +} + +ruby_version=$(cat .ruby-version) +owner=$(whoami) + +echo "Starting container using Ruby $ruby_version..." +container_id=$( + docker run --network="host" \ + --env PGHOST --env PGUSER --env PGPASSWORD \ + -d -v .:/app ruby:"$ruby_version" sleep infinity +) + +echo "Check the environment is correct..." +run_in_container "$container_id" "env" + +echo "Run bundle install..." +run_in_container "$container_id" "bundle install" + +echo "Running db:setup..." +run_in_container "$container_id" "bundle exec rake db:setup" + +echo "Tidying up container..." +docker stop "$container_id" > /dev/null +docker rm "$container_id" > /dev/null + +echo "Restoring file permissions..." +sudo chown -R "$owner" .