Skip to content

Commit

Permalink
Split CI builds by gems at top-level (#1249)
Browse files Browse the repository at this point in the history
* fix: remove unneeded Appraisals for opentelemetry-registry

It's not actually doing anything, so we skip it.

* ci: remove ci-without-services.yml

We're going to bring back these jobs in the next few commits, but we can delete it right now.

* ci: remove toys/ci.rb

We're going to replicate this in Actions natively, so that we can get
more comprehensible build output.

* ci: replace toys.rb functionality with an explosion of actions + yaml

This replaces the "test it all in a loop" approach that `toys/ci.rb` was
taking, by leveraging some more advanced features of GitHub Actions.

To start, we construct a custom Action (not a workflow!) that can run
all the tests we were doing with `toys/ci.rb`. It takes a few different
inputs: gem to test, ruby version to use, whether or not to do rubocop,
etc. Then, it figures out where in the repo that gem lives, sets up ruby
(including appraisals setup, if necessary), and runs rake tests (and
then conditionally runs YARD, rubocop, etc).

Then, over in `ci.yml`, we list out all of the gems we currently have
and chunk them up into different logical groups:

- base (api, sdk, etc)
- exporters
- propagators
- instrumentation that requires sidecar services to test
- instrumentaiton that doesn't require anything special to test

For most groups, we set up a matrix build of operating systems (ubuntu,
macos, and windows) - except for the "instrumentation_with_services"
group, because sidecar services are only supported on linux.

For each matrix group (gem + os), we then have a build that has multiple
steps - and each step calls the custom Action that we defined earlier,
passing appropriate inputs. Each step tests a different ruby version:
3.1, 3.0, 2.7, or jruby - and we conditionally skip the step based on
the operating system (we only run tests against ruby 3.1 for mac /
windows, because the runners are slower and we can't launch as many at
once).

Notably, we have a few matrix exclusions here: things that wont build on
macos or windows, but there aren't many.

Finally, each group also maintains a "skiplist" of sorts for jruby -
it's ugly, but some instrumentation just doesn't work for our Java
friends. So we have a step that tests whether or not we should build the
gem for jruby, and then the jruby step is skipped depending on the
answer. We can't really use a matrix exclusion here because we don't use
the ruby version in the matrix at all - otherwise we'd have a *huge*
explosion of jobs to complete, when in reality we can actually install +
test multiple ruby versions on a single runner, if we're careful.

The net effect of all of this is that we end up having many different
builds running in parallel, and if a given gem fails we can *easily* see
that and get right to the problem. Builds are slightly faster, too.

The major downsides are:
- We need to add new gems to the build list when we create them.
- We can't cache gems for appraisals, which adds a few minutes onto the
  build times (to be fair, we weren't caching anything before)
- It's just kinda unwieldy.
- I didn't improve anything around the actual release process yet.

Future improvements could be:
- Figuring out how to cache things with Appraisals, because I gave up
  after a whole morning of fighting bundler.
- Dynamically generating things again, because it's annoying to add gems
  to the build matrices.

* feat: add scary warning to instrumentation_generator re: CI workflows

* fix: remove testing change

* ci: Add note about instrumentation_with_services
  • Loading branch information
ahayworth authored May 13, 2022
1 parent 9e7efb7 commit 45429c7
Show file tree
Hide file tree
Showing 6 changed files with 440 additions and 251 deletions.
130 changes: 130 additions & 0 deletions .github/actions/test_gem/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Test Gem
description: Tests a specific Gem
inputs:
gem:
description: Gem to test
required: true
type: string
ruby:
description: Ruby version to use
required: true
type: string
yard:
description: Run YARD documentation
required: false
type: boolean
default: false
rubocop:
description: Run Rubocop
required: false
type: boolean
default: false
build:
description: Build gem
required: false
type: boolean
default: false

runs:
using: composite
steps:
- name: Setup
id: setup
shell: bash
run: |
dir=$(find . -iname '${{ inputs.gem }}.gemspec' -exec dirname {} \;)
echo "::set-output name=dir::${dir}"
# We install multiple ruby versions here, and that makes for some
# annoying bundler conflicts when we get to the JRuby step. Removing
# the lockfile slows things down a bit, but we should still get most
# of the benefits of bundler caching.
rm -f "${dir}/Gemfile.lock"
echo "::set-output name=cache_key::mri"
if [[ "${{ inputs.ruby }}" == "jruby" ]]; then
echo "::set-output name=cache_key::jruby"
fi
echo "::set-output name=appraisals::false"
if [[ -f "${dir}/Appraisals" ]]; then
echo "::set-output name=appraisals::true"
fi
# Install ruby and bundle dependencies and cache!
# ...but not for appraisals, sadly.
- name: Install Ruby ${{ inputs.ruby }} with dependencies
if: "${{ steps.setup.outputs.appraisals == 'false' }}"
uses: ruby/setup-ruby@v1
with:
ruby-version: "${{ inputs.ruby }}"
working-directory: "${{ steps.setup.outputs.dir }}"
bundler-cache: true
cache-version: "v1-${{ steps.setup.outputs.cache_key }}"

# If we're using appraisals, do it all manually.
- name: Install Ruby ${{ inputs.ruby }} without dependencies
if: "${{ steps.setup.outputs.appraisals == 'true' }}"
uses: ruby/setup-ruby@v1
with:
ruby-version: "${{ inputs.ruby }}"
- name: Install dependencies and appraisals
if: "${{ steps.setup.outputs.appraisals == 'true' }}"
shell: bash
run: |
cd "${{ steps.setup.outputs.dir }}"
bundle install
bundle exec appraisal install
- name: Test Gem
shell: bash
run: |
cd "${{ steps.setup.outputs.dir }}"
if [[ -f "Appraisals" ]]; then
bundle exec appraisal rake test
else
bundle exec rake test
fi
env:
TEST_KAFKA_HOST: "127.0.0.1"
TEST_KAFKA_PORT: 29092
TEST_MYSQL_HOST: "127.0.0.1"
TEST_MYSQL_PORT: 3306
TEST_MYSQL_USER: mysql
TEST_MYSQL_PASSWORD: mysql
TEST_POSTGRES_PASSWORD: postgres
TEST_POSTGRES_USER: postgres
TEST_POSTGRES_HOST: localhost
TEST_POSTGRES_PORT: 5432
TEST_POSTGRES_DB: postgres
TEST_MEMCACHED_HOST: localhost
TEST_MEMCACHED_PORT: 11211
TEST_MONGODB_HOST: localhost
TEST_MONGODB_PORT: 27017
TEST_RABBITMQ_HOST: localhost
TEST_RABBITMQ_PORT: 5672
TEST_RABBITMQ_URL: amqp://guest:guest@localhost:5672
TEST_REDIS_HOST: localhost
TEST_REDIS_PORT: 6379

- name: YARD
shell: bash
if: "${{ inputs.yard == 'true' }}"
run: |
cd "${{ steps.setup.outputs.dir }}"
bundle exec rake yard
- name: Rubocop
shell: bash
if: "${{ inputs.rubocop == 'true' }}"
run: |
cd "${{ steps.setup.outputs.dir }}"
bundle exec rake rubocop
- name: Build Gem
shell: bash
if: "${{ inputs.build == 'true' }}"
run: |
cd "${{ steps.setup.outputs.dir }}"
gem build ${{ inputs.gem }}.gemspec
74 changes: 0 additions & 74 deletions .github/workflows/ci-without-services.yml

This file was deleted.

Loading

0 comments on commit 45429c7

Please sign in to comment.