|
| 1 | +# The CI build, in detail |
| 2 | + |
| 3 | +The [Travis CI build](https://travis-ci.org/rspec/<%= project_name %>) |
| 4 | +runs many verification steps to prevent regressions and |
| 5 | +ensure high-quality code. To run the Travis build locally, run: |
| 6 | + |
| 7 | +``` |
| 8 | +$ script/run_build |
| 9 | +``` |
| 10 | + |
| 11 | +It can be useful to run the build steps individually |
| 12 | +to repro a failing part of a Travis build. Let's break |
| 13 | +the build down into the individual steps. |
| 14 | + |
| 15 | +## Specs |
| 16 | + |
| 17 | +RSpec dogfoods itself. Its primary defense against regressions is its spec suite. Run with: |
| 18 | + |
| 19 | +``` |
| 20 | +$ bundle exec rspec |
| 21 | + |
| 22 | +# or, if you installed your bundle with `--standalone --binstubs`: |
| 23 | + |
| 24 | +$ bin/rspec |
| 25 | +``` |
| 26 | + |
| 27 | +The spec suite performs a couple extra checks that are worth noting: |
| 28 | + |
| 29 | +* *That all the code is warning-free.* Any individual example that produces output |
| 30 | + to `stderr` will fail. We also have a spec that loads all the `lib` and `spec` |
| 31 | + files in a newly spawned process to detect load-time warnings and fail if there |
| 32 | + are any. RSpec must be warning-free so that users who enable Ruby warnings will |
| 33 | + not get warnings from our code. |
| 34 | +* *That only a minimal set of stdlibs are loaded.* Since Ruby makes loaded libraries |
| 35 | + available for use in any context, we want to minimize how many bits of the standard |
| 36 | + library we load and use. Otherwise, RSpec's use of part of the standard library could |
| 37 | + mask a problem where a gem author forgets to load a part of the standard library they |
| 38 | + rely on. The spec suite contains a spec that defines a whitelist of allowed loaded |
| 39 | + stdlibs. |
| 40 | + |
| 41 | +In addition, we use [SimpleCov](https://github.com/colszowka/simplecov) |
| 42 | +to measure and enforce test coverage. If the coverage falls below a |
| 43 | +project-specific threshold, the build will fail. |
| 44 | + |
| 45 | +## Cukes |
| 46 | + |
| 47 | +RSpec uses [cucumber](https://cucumber.io/) for both acceptance testing |
| 48 | +and [documentation](https://relishapp.com/rspec). Since we publish our cukes |
| 49 | +as documentation, please limit new cucumber scenarios to user-facing examples |
| 50 | +that help demonstrate usage. Any tests that exist purely to prevent regressions |
| 51 | +should be written as specs, even if they are written in an acceptance style. |
| 52 | +Duplication between our YARD API docs and the cucumber documentation is fine. |
| 53 | + |
| 54 | +Run with: |
| 55 | + |
| 56 | +``` |
| 57 | +$ bundle exec cucumber |
| 58 | + |
| 59 | +# or, if you installed your bundle with `--standalone --binstubs`: |
| 60 | + |
| 61 | +$ bin/cucumber |
| 62 | +``` |
| 63 | + |
| 64 | +## YARD documentation |
| 65 | + |
| 66 | +RSpec uses [YARD](http://yardoc.org/) for API documentation on the [rspec.info site](http://rspec.info/). |
| 67 | +Our commitment to [SemVer](htp://semver.org) requires that we explicitly |
| 68 | +declare our public API, and our build uses YARD to ensure that every |
| 69 | +class, module and method has either been labeled `@private` or has at |
| 70 | +least some level of documentation. For new APIs, this forces us to make |
| 71 | +an intentional decision about whether or not it should be part of |
| 72 | +RSpec's public API or not. |
| 73 | + |
| 74 | +To run the YARD documentation coverage check, run: |
| 75 | + |
| 76 | +``` |
| 77 | +$ bundle exec yard stats --list-undoc |
| 78 | + |
| 79 | +# or, if you installed your bundle with `--standalone --binstubs`: |
| 80 | + |
| 81 | +$ bin/yard stats --list-undoc |
| 82 | +``` |
| 83 | + |
| 84 | +We also want to prevent YARD errors or warnings when actually generating |
| 85 | +the docs. To check for those, run: |
| 86 | + |
| 87 | +``` |
| 88 | +$ bundle exec yard doc --no-cache |
| 89 | + |
| 90 | +# or, if you installed your bundle with `--standalone --binstubs`: |
| 91 | + |
| 92 | +$ bin/yard doc --no-cache |
| 93 | +``` |
| 94 | + |
| 95 | +## Rubocop |
| 96 | + |
| 97 | +We use [Rubocop](https://github.com/bbatsov/rubocop) to enforce style conventions on the project so |
| 98 | +that the code has stylistic consistency throughout. Run with: |
| 99 | + |
| 100 | +``` |
| 101 | +$ bundle exec rubocop lib |
| 102 | + |
| 103 | +# or, if you installed your bundle with `--standalone --binstubs`: |
| 104 | + |
| 105 | +$ bin/rubocop lib |
| 106 | +``` |
| 107 | + |
| 108 | +Our Rubocop configuration is a work-in-progress, so if you get a failure |
| 109 | +due to a Rubocop default, feel free to ask about changing the |
| 110 | +configuration. Otherwise, you'll need to address the Rubocop failure, |
| 111 | +or, as a measure of last resort, by wrapping the offending code in |
| 112 | +comments like `# rubocop:disable SomeCheck` and `# rubocop:enable SomeCheck`. |
| 113 | + |
| 114 | +## Run spec files one-by-one |
| 115 | + |
| 116 | +A fast TDD cycle depends upon being able to run a single spec file, |
| 117 | +without the rest of the test suite. While rare, it's fairly easy to |
| 118 | +create a situation where a spec passes when the entire suite runs |
| 119 | +but fails when its individual file is run. To guard against this, |
| 120 | +our CI build runs each spec file individually, using a bit of bash like: |
| 121 | + |
| 122 | +``` |
| 123 | +for file in `find spec -iname '*_spec.rb'`; do |
| 124 | + echo "Running $file" |
| 125 | + bin/rspec $file -b --format progress |
| 126 | +done |
| 127 | +``` |
| 128 | + |
| 129 | +Since this step boots RSpec so many times, it runs much, much |
| 130 | +faster when we can avoid the overhead of bundler. This is a main reason our |
| 131 | +CI build installs the bundle with `--standalone --binstubs` and |
| 132 | +runs RSpec via `bin/rspec` rather than `bundle exec rspec`. |
| 133 | + |
| 134 | +## Running the spec suite for each of the other repos |
| 135 | + |
| 136 | +While each of the RSpec repos is an independent gem (generally designed |
| 137 | +to be usable on its own), there are interdependencies between the gems, |
| 138 | +and the specs for each tend to use features from the other gems. We |
| 139 | +don't want to merge a pull request for one repo that might break the |
| 140 | +build for another repo, so our CI build includes a spec that runs the |
| 141 | +spec suite of each of the _other_ project repos. Note that we only run |
| 142 | +the spec suite, not the full build, of the other projects, as the spec |
| 143 | +suite runs very quickly compared to the full build. |
| 144 | + |
0 commit comments